Creating Custom Exceptions using Django Rest Framework
Tags: django
Published: 2019-08-19 21:13:15

Serialization errors returned by [Django Rest Framework](https://www.django-rest-framework.org/) are not simple for the frontend to display, IMO. Here is how it return serialization errors for common user signup errors:

```
{'username': ['This field is required.'], 'password': ['This field must be at least 8 chars'], 'email': ['This field cannot be blank']}

```
**I find this hard for the frontend to have to handle.** It might have to do a lot of work to say which field the error message relates to. It's also cumbersome for the frontend to parse a bunch of arrays for errors. 

To make the error messages friendlier:

First, override the default error messages in the `__init__` method of your serializer. Here I am changing it so that it actually **says what field (i.e. username instead of 'This field') the error is related to**:

```python
class UserSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(required=True)
    username = serializers.CharField(required=True)
    password = serializers.CharField(min_length=8, required=True)

    class Meta:
        model = User
        fields = ("id", "username", "email", "password")

    def __init__(self, *args, **kwargs):
        super(UserSerializer, self).__init__(*args, **kwargs)
        self.fields["username"].error_messages["required"] = u"username is required"
        self.fields["username"].error_messages["blank"] = u"username cannot be blank"
        self.fields["email"].error_messages["required"] = u"email is required"
        self.fields["email"].error_messages["blank"] = u"email cannot be blank"
        self.fields["password"].error_messages[
            "min_length"
        ] = u"password must be at least 8 chars"
```

Then **define the custom exception**, which I did in a new file called `exceptions.py.` I am looking at the fields I might want to override an error and grabbing the first item in the array. If there are multiple exceptions per field, I'm not concerned because if the user fixes one, they'll graduate to the next exception. 

```python
from rest_framework.exceptions import ValidationError
from rest_framework.views import exception_handler


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

    # check that a ValidationError exception is raised
    if isinstance(exc, ValidationError):
        # here prepare the 'custom_error_response' and
        # set the custom response data on response object
        if response.data.get("username", None):
            response.data = response.data["username"][0]
        elif response.data.get("email", None):
            response.data = response.data["email"][0]
        elif response.data.get("password", None):
            response.data = response.data["password"][0]

    return response
```

**Finally, make sure to specify in your `settings.py` to use your new exception handler**:

```python
REST_FRAMEWORK = {
    "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
    "DEFAULT_AUTHENTICATION_CLASSES": (
        "rest_framework.authentication.TokenAuthentication",
        "rest_framework.authentication.SessionAuthentication",
        "rest_framework.authentication.BasicAuthentication",
    ),
    "EXCEPTION_HANDLER": ("app.exceptions.base_exception_handler"),
}
```

Here is the friendlier error message!
![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/hbhzyo25tiyyrudta82q.png)