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?
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.
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".
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.
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.
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)
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With