Overwrite django rest default validation errors handler

Viewed 96

I am using django-rest for my back-end and want to overwrite default errors for fields. My current code looks like this.

class DeckSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product

        fields = (
            "id",
            "title",
            "get_absolute_url",
            "description",
            "price",
            "image",
            "category_id",
            "category",
            "title"
        )
        extra_kwargs = {
            'title': {"error_messages": {"required": "Title cannot be empty"}},
            'image': {"error_messages": {"required": "Image cannot be empty"},}


        }

After writing these 2 kwargs i realised i would just be repeating something that could be solved by code.

By default the serializer validation returns this when the field is missing {title:"This field is required"}.

Is there any way that i can overwrite the current message so it can display directly the name_of_the_field + my_message . Example {title: Title is required}

I am not looking on how to write custom error message for a single field , im looking on how to write generic costum messages for every field that for example is missing or null.

1 Answers

We can achieve it by writing a custom exception handler.

Here is how a custom response might look like:

{
    "status_code": 400,
    "type": "ValidationError",
    "message": "Bad request syntax or unsupported method",
    "errors": [
        "username: This field may not be null.",
        "email: This field may not be null.",
        "ticket number: This field may not be null."
    ]
}

We have to create a file: exception_handler.py in our project directory with the code that follows; I use utils for this kind of purposes. You can also put this code anywhere you like, but I prefer to have it in a separated file dedicated for this purpose.

from http import HTTPStatus

from rest_framework import exceptions
from rest_framework.views import Response, exception_handler


def api_exception_handler(exception: Exception, context: dict) -> Response:
    """Custom API exception handler."""

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

    # Only alter the response when it's a validation error
    if not isinstance(exception, exceptions.ValidationError):
        return response

    # It's a validation error, there should be a Serializer
    view = context.get("view", None)
    serializer = view.get_serializer_class()()

    errors_list = []
    for key, details in response.data.items():

        if key in serializer.fields:
            label = serializer.fields[key].label
            help_text = serializer.fields[key].help_text

            for message in details:
                errors_list.append("{}: {}".format(label, message))

        elif key == "non_field_errors":
            for message in details:
                errors_list.append(message)

        else:
            for message in details:
                errors_list.append("{}: {}".format(key, message))

    # Using the description's of the HTTPStatus class as error message.
    http_code_to_message = {v.value: v.description for v in HTTPStatus}

    error_payload = {
        "status_code": 0,
        "type": "ValidationError",
        "message": "",
        "errors": [],
    }

    #  error = error_payload["error"]
    status_code = response.status_code

    error_payload["status_code"] = status_code
    error_payload["message"] = http_code_to_message[status_code]
    error_payload["errors"] = errors_list

    # Overwrite default exception_handler response data
    response.data = error_payload

    return response

The main idea comes from here, but I changed it to my needs. change it as you see fit.

Don't forget to set it as your default exception handler in you settings.py file:

REST_FRAMEWORK["EXCEPTION_HANDLER"] = "utils.exception_handler.api_exception_handler";
Related