How to Customize Validation Error Response in Django REST Framework?

I am developing a RESTful API using Django REST Framework (DRF) and I need to implement a common error handling mechanism for various validation errors that occur throughout the application, including but not limited to JWT authentication failures.

when validation errors occur, DRF returns default error responses that may not be user-friendly or consistent across different endpoints. For example, a JWT token validation error produces a response that looks like this:

{
    "detail": "Given token not valid for any token type",
    "code": "token_not_valid",
    "messages": [
        {
            "token_class": "AccessToken",
            "token_type": "access",
            "message": "Token is invalid or expired"
        }
    ]
}

I would like to standardize the error responses throughout my application to improve the user experience. Specifically, I want to create a common function that can return a structured error response for all validation errors in the following format:

{
    "status": false,
    "responseCode": 0,
    "message": "Validation ",
    "data": {}
}

I tried by creating custom_exception_handler

def custom_exception_handler(exc, context):
    response = exception_handler(exc, context)

    if response is not None:
        custom_response_data = {
            'status': False,
            'responseCode': 0,
            'message': 'Validation error',
            'data': {}
        }
        
        if isinstance(response.data, dict):
            errors = {}
            for field, detail in response.data.items():
                if isinstance(detail, list):
                    errors[field] = detail[0] if detail else 'Invalid input.'
                elif isinstance(detail, dict):
                    errors[field] = " | ".join(str(v) for v in detail.values())
                else:
                    errors[field] = str(detail)

            custom_response_data['message'] = " | ".join(errors.values())
            custom_response_data['data'] = errors

        response.data = custom_response_data
    return response

But its not giving what i want

In DRF validation is made all inside the serializer. In a shallow example, suppose you want to generate a JWT token for a given user:

models.py

class JWTExample(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    token = models.UUIDField(default=uuid.uuid4, editable=False)
    refresh_token = models.UUIDField(default=uuid.uuid4, editable=False)

views.py

class JWTCreateView(APIView):
    def post(self, request, format=None):
        serializer = JWTExampleSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        return Response(serializer.data)

There are two ways of implementing custom validators either as a function-based validator or as a class-based. For reusing you can export this function / class into a validators.py file. Since function based are straight forward here is a class-based example:

validators.py

class UserValidator:
    def __call__(self, value):
        try:
            user = get_user_model().objects.get(username=value)
        except ObjectDoesNotExist:
            message = {
                "status": False,
                "responseCode": 0,
                "message": "Validation ",
                "data": {},
            }
            raise serializers.ValidationError(message)

serializers.py

class JWTExampleSerializer(serializers.Serializer):
    user = serializers.CharField()
    token = serializers.UUIDField(required=False)
    refresh_token = serializers.UUIDField(required=False)

    def validate_user(self, value):
        validate_user = UserValidator()
        validate_user(value)
Back to Top