Format ValidationError in Serializer
Asked Answered
P

2

6

DjangoRestFramework seems to handle errors with a variety of ways. The ValidationError in the serializer class does not consistently return JSON the same.

Current response includes a JSON list/object string:

{"detail":["Unable to log in with provided credentials."]}

Looking to achieve:

{"detail":"Unable to log in with provided credentials."}

I realize that this response is a result of default functions. However, I've overridden the validate function:

class AuthCustomTokenSerializer(serializers.Serializer):
username = serializers.CharField(write_only=True)
password = serializers.CharField(write_only=True)
token = serializers.CharField(read_only=True)

def validate(self, validated_data):
    username = validated_data.get('username')
    password = validated_data.get('password')

    # raise serializers.ValidationError({'detail': 'Unable to log in with provided credentials.'})

    if username and password:
        user = authenticate(phone_number=username, password=password)

        try:

            if UserInfo.objects.get(phone_number=username):
                userinfo = UserInfo.objects.get(phone_number=username)
                user = User.objects.filter(user=userinfo.user, password=password).latest('date_joined')

            if user:

                if user.is_active:
                    validated_data['user'] = user
                    return validated_data

                else:
                    raise serializers.ValidationError({"detail": "User account disabled."})

        except UserInfo.DoesNotExist:
            try:
                user = User.objects.filter(email=username, password=password).latest('date_joined')

                if user.is_active:
                    validated_data['user'] = user
                    return validated_data

            except User.DoesNotExist:
                #raise serializers.ValidationError("s")
                raise serializers.ValidationError({'detail': 'Unable to log in with provided credentials.'})
    else:
        raise serializers.ValidationError({"detail" : "Must include username and password."})

class Meta:
    model = Token
    fields = ("username", "password", "token")

I've tried adding a custom exception handler:

from rest_framework.views import exception_handler

def custom_exception_handler(exc, context):
    # Call REST framework's default exception handler first,
    # to get the standard error response.
    response = exception_handler(exc, context)

    # Now add the HTTP status code to the response.
    if response is not None:
        response.data['status_code'] = response.status_code


    return response

views.py: if serializer.is_valid(raise_exception=True):

However, that only appends the currently raised error:

{"detail":["Unable to log in with provided credentials."],"status_code":400}

How should I use change the format of the returning text? It only returns the JSON like this for this particular serializer within the validate function.

I've also looked into formatting the non_field_errors template, but it works with all my other serializers e.g:

{"detail": "Account exists with email address."}
Pastern answered 22/2, 2016 at 18:26 Comment(0)
D
0

Maybe you should try overriding json renderer class and hook up a custom one, where you can check for status code and detail key in response data, then re-format the value appropriately.

I never tried that, so I can't give you the exact codebase, but this is the only approach I can think of to have consistent response.

Discernment answered 22/2, 2016 at 20:57 Comment(2)
I've added a custom JSON renderer class and using it with my serializer class. I've reformatted the renderer but its very case specific as in it doesn't work if there's no JSON array being used: def render(self, data, accepted_media_type=None, renderer_context=None): data = { 'renderer_test' : data['detail'][0] } return super(CustomJSONRenderer, self).render(data, accepted_media_type, renderer_context) Returns this which is what I am looking for: {"renderer_test":"Unable to log in with provided credentials."} But gets a KeyError with "detail"Pastern
Shouldn't there be an easy way to avoid using arrays within JSON when raising ValidationErrors? What is the convention for JSON error responses via DRF? Would it make more sense to create the single object array as the detail's value for all error?Pastern
V
0

You can try handling Validation errors in your custom_exception_handler function

def custom_exception_handler(exc, context):
    # Call REST framework's default exception handler first,
    # to get the standard error response.
    response = exception_handler(exc, context)

    # Now add the HTTP status code to the response.
    if response is not None:
       # If it is a Validation Error
       if response.status_code == 400:
          errors_dict = e.get_full_details()
          final_message = {}
          for key, value in errors_dict.items():
             final_message[key] = value[0].get('message', '')
          response.data['detail'] = final_message
          response.data['status_code'] = response.status_code


    return response

Worked for me. The only difference was, I caught this exception in my custom decorator function and added the decorator in serializer functions incrementally.

Vandusen answered 17/1 at 16:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.