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

45👍

✅

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)

17👍

By default, raising serializers.ValidationError will return with HTTP_400_BAD_REQUEST

But sometimes we would like to return ValidationError with normal 200 status code, because some libraries on the client side can’t parse json response data while response code is not 200.

I tried this. but it’s not worked:

raise serializers.ValidationError({'message':'Invalid  email address'}, code=200)

So we can do this and it works:

res = serializers.ValidationError({'message':'Invalid  email address'})
res.status_code = 200
raise res

4👍

Use django-rest-framework custom exception handler http://www.django-rest-framework.org/api-guide/exceptions/

def custom_exception_handler(exc, context=None):
    response = exception_handler(exc, context)
    if response is not None:
         if response.data['detail'] == 'Duplicate Username':
            response.data['username'] = response.data.pop('detail')
        response.status_code = status.HTTP_409_CONFLICT
    return response

0👍

To add to Anush Devendra’s answer, it seems that raising anything else than a ValidationError will bypass the treatment done by DRF on other fields.

Considering this code from DRF in exceptions.py:

def to_internal_value(self, data):
        [...]

        for field in fields:
            [...]
            try:
                validated_value = field.run_validation(primitive_value)
                if validate_method is not None:
                    validated_value = validate_method(validated_value)
            except ValidationError as exc:
                errors[field.field_name] = exc.detail
            [...]
            else:
                set_value(ret, field.source_attrs, validated_value)

        if errors:
            raise ValidationError(errors)

        return ret

If you want to have this kind of answer:

{
    "my_first_field": [
        "The first field had an error."
    ],
    "my_second_field": [
        "The second field had an error."
    ],
}

you need to raise a ValidationError in the validate_<field>() methods.

Note that doing so you won’t be able to have a custom error heriting from ValidationError and with a status_code different of 400. Your detail message will be extract and a new ValidationError (with a default 400 status_code) raise.

Leave a comment