djangodjango-rest-framework

Django REST Framework how to specify error code when raising validation error in serializer


I have an API endpoint that allow users to register an account. I would like to return HTTP 409 instead of 400 for a duplicate username.

Here is my serializer:

from django.contrib.auth.models import User
from rest_framework.serializers import ModelSerializer

class UserSerializer(ModelSerializer):
    username = CharField()

    def validate_username(self, value):
        if User.objects.filter(username=value).exists():
            raise NameDuplicationError()
        return value


class NameDuplicationError(APIException):
    status_code = status.HTTP_409_CONFLICT
    default_detail = u'Duplicate Username'

When the error is triggered, the response is: {"detail":"Duplicate Username"}. I realised that if I subclass APIException, the key detail is used instead of username.

I want to have this response instead {"username":"Duplicate Username"}

or I would like to specify a status code when raising a ValidationError:

def validate_username(self, value):
    if User.objects.filter(username=value).exists():
        raise serializers.ValidationError('Duplicate Username', 
                                          status_code=status.HTTP_409_CONFLICT)
    return value

But this does not work as ValidationError only returns 400.

Is there any other way to accomplish this?


Solution

  • You can raise different exceptions like:

    from rest_framework.exceptions import APIException
    from django.utils.encoding import force_text
    from rest_framework import status
    
    
    class CustomValidation(APIException):
        status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
        default_detail = 'A server error occurred.'
    
        def __init__(self, detail, field, status_code):
            if status_code is not None:self.status_code = status_code
            if detail is not None:
                self.detail = {field: force_text(detail)}
            else: self.detail = {'detail': force_text(self.default_detail)}
    

    you can use this in your serializer like:

    raise CustomValidation('Duplicate Username','username', status_code=status.HTTP_409_CONFLICT)
    

    or

    raise CustomValidation('Access denied','username', status_code=status.HTTP_403_FORBIDDEN)