Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 793
Cheng Avatar asked Nov 02 '15 10:11

Cheng


People also ask

How do you increase validation error in DRF?

The easiest way to change the error style through all the view in your application is to always use serializer. is_valid(raise_exception=True) , and then implement a custom exception handler that defines how the error response is created.

How do I increase error in Django REST framework?

The generic views use the raise_exception=True flag, which means that you can override the style of validation error responses globally in your API. To do so, use a custom exception handler, as described above. By default this exception results in a response with the HTTP status code "400 Bad Request".

How does Django validate data in serializer?

Validation in Django REST framework serializers is handled a little differently to how validation works in Django's ModelForm class. With ModelForm the validation is performed partially on the form, and partially on the model instance. With REST framework the validation is performed entirely on the serializer class.

How do I handle exceptions in Django?

Django Exception Classes The base class for DoesNotExist exceptions. If a query does not return any result, this exception is raised. It raises when the requested field does not exist. This exception is raised by a query if only one object is expected, but multiple objects are returned.


3 Answers

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)
like image 177
Anush Devendra Avatar answered Oct 13 '22 06:10

Anush Devendra


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
like image 10
Omid Raha Avatar answered Oct 13 '22 05:10

Omid Raha


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
like image 4
mntl_ Avatar answered Oct 13 '22 05:10

mntl_