pythondjangodjango-rest-frameworkdjango-serializerdjango-validation

How to raise 404 as a status code in Serializer Validate function in DRF?


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?


Solution

  • 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