Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework - how to create custom error messages for all ModelSerializer fields?

This is my serializers.py (I want to create a serializer for the built-in User model):

from rest_framework import serializers

from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):

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

I'm aware that Django Rest Framework has it's own field validators, because when I try to create a user using a username which already exists, it raises an error saying:

{'username': [u'This field must be unique.']}

I want to customize the error message and make it say "This username is already taken. Please try again" rather than saying "This field must be unique".

It also has a built-in regex validator, because when I create a username with an exclamation mark, it says:

{'username': [u'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.']}

I want to customize the regex validator so that it just says "Invalid username".

How do I customize all of the error messages which each field has?

Note: according to this post: Custom error messages in Django Rest Framework serializer I can do:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User

    def __init__(self, *args, **kwargs):
        super(UserSerializer, self).__init__(*args, **kwargs)

        self.fields['username'].error_messages['required'] = u'My custom required msg'

But what do I do for the 'unique' and 'regex' validators? I tried doing

self.fields['username'].error_messages['regex'] = u'My custom required msg'

and

self.fields['username'].error_messages['validators'] = u'My custom required msg'

but neither worked.

like image 216
SilentDev Avatar asked Jun 01 '15 03:06

SilentDev


2 Answers

In order to replace unique or regex error messages you should change message member of corresponding validator object. This could be done using separate mixin class:

from django.core.validators import RegexValidator
from rest_framework.validators import UniqueValidator
from django.utils.translation import ugettext_lazy as _


class SetCustomErrorMessagesMixin:
    """
    Replaces built-in validator messages with messages, defined in Meta class. 
    This mixin should be inherited before the actual Serializer class in order to call __init__ method.

    Example of Meta class:

    >>> class Meta:
    >>>     model = User
    >>>     fields = ('url', 'username', 'email', 'groups')
    >>>     custom_error_messages_for_validators = {
    >>>         'username': {
    >>>             UniqueValidator: _('This username is already taken. Please, try again'),
    >>>             RegexValidator: _('Invalid username')
    >>>         }
    >>>     }
    """
    def __init__(self, *args, **kwargs):
        # noinspection PyArgumentList
        super(SetCustomErrorMessagesMixin, self).__init__(*args, **kwargs)
        self.replace_validators_messages()

    def replace_validators_messages(self):
        for field_name, validators_lookup in self.custom_error_messages_for_validators.items():
            # noinspection PyUnresolvedReferences
            for validator in self.fields[field_name].validators:
                if type(validator) in validators_lookup:
                    validator.message = validators_lookup[type(validator)]

    @property
    def custom_error_messages_for_validators(self):
        meta = getattr(self, 'Meta', None)
        return getattr(meta, 'custom_error_messages_for_validators', {})

Then you could just inherit this mixin and update Meta class:

class UserSerializer(SetCustomErrorMessagesMixin, serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ('url', 'username', 'email', 'groups')
        custom_error_messages_for_validators = {
            'username': {
                UniqueValidator: _('This username is already taken. Please, try again'),
                RegexValidator: _('Invalid username')
            }
        }
like image 106
awesoon Avatar answered Oct 30 '22 21:10

awesoon


You also can do that just customizing your language translation, look here:

https://docs.djangoproject.com/en/3.2/topics/i18n/translation/#message-files

All you have to do is to create message files, change translation in there and compile your message into .mo files.

This method is not so much unacceptable as i might seems. Look here:

https://www.django-rest-framework.org/topics/internationalization/

DRF docs say that its common practice customizing error messages (your own and default ones) via translation.

My tips for those who`d want to try path above:

  1. Make default django.po file. Most of default errors will be there.
  2. If you are using DRF, then open this file https://raw.githubusercontent.com/encode/django-rest-framework/master/rest_framework/locale/en_US/LC_MESSAGES/django.po , chose error that you want to customize and copy only that error into your django.po flle above.
  3. Customize it.
  4. Compile it.

(!) NOTE Make sure that your translation file named exacly as django.po, not mylovelydjango.po, not myproject.po but EXACTLY django.po, or nothing will work.

like image 32
Artem Chege Avatar answered Oct 30 '22 21:10

Artem Chege