I have written a validate() function inside my serializer. By default, Serializer Errors return 400 as a status code. But I want to return 404. I tried this:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = models.MyClass
fields = "__all__"
def validate(self, data):
current_user = self.context.get("request").user
user = data.get("user")
if user!=current_user:
raise ValidationError({'detail': 'Not found.'}, code=404)
return data
But it still returns 400 as a response in status code. How to do it?
CodePudding user response:
This is the Django source code that handles validation errors.
def is_valid(self, *, raise_exception=False):
# This implementation is the same as the default,
# except that we use lists, rather than dicts, as the empty case.
assert hasattr(self, 'initial_data'), (
'Cannot call `.is_valid()` as no `data=` keyword argument was '
'passed when instantiating the serializer instance.'
)
if not hasattr(self, '_validated_data'):
try:
self._validated_data = self.run_validation(self.initial_data)
except ValidationError as exc:
self._validated_data = []
self._errors = exc.detail
else:
self._errors = []
if self._errors and raise_exception:
raise ValidationError(self.errors)
return not bool(self._errors)
Django checks for ValidationError, then takes the details from that error object and re-creates with a 400 status code, which is why your code does not work.
So, from your view class, catch the django error and change the status code as shown.
class NotesList(mixins.CreateModelMixin, mixins.ListModelMixin, generics.GenericAPIView):
serializer_class = MySerializer
def get_queryset(self):
return models.MyClass.objects.filter(owner=self.request.user)
def post(self, request):
try:
return self.create(request)
except ValidationError as exc:
exc.status_code = 404
raise exc
CodePudding user response:
You can do it from view by handling the serializes validation:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = models.MyClass
fields = "__all__"
def validate(self, data):
current_user = self.context.get("request").user
user = data.get("user")
if user != current_user:
raise serializers.ValidationError(
{'detail': 'Not found.'},
code=404,
)
return data
class MyView(APIView):
def post(self, request):
serializer = MySerializer(
data=request.data,
context={'request': request},
)
if not serializer.is_valid():
return Response(serializer.errors, status=404)
return Response(serializer.data)
Alternately you can do it by defining own exception handler:
def my_exception_handler(exc, context):
response = exception_handler(exc, context)
if response is not None and response.status_code == 400:
response.status_code = 404
return response
class MyView(APIView):
exception_handler = my_exception_handler
def post(self, request):
serializer = MySerializer(
data=request.data,
context={'request': request},
)
serializer.is_valid(raise_exception=True)
return Response(serializer.data)
But as you said, if you want to do it from serializer, then you have to define custom exception and raise it from serialize :
class NotFoundError(serializers.ValidationError):
def __init__(self, detail):
super().__init__(detail, code=404)
class MySerializer(serializers.ModelSerializer):
class Meta:
model = models.MyClass
fields = "__all__"
def validate(self, data):
current_user = self.context.get("request").user
user = data.get("user")
if user != current_user:
raise NotFoundError({'detail': 'Not found.'})
return data