9

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.

2 Answers 2

6

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')
            }
        }
Sign up to request clarification or add additional context in comments.

9 Comments

Another way is replacing all validators for a field using Meta.extra_kwargs field. Note, that you should provide all validators, because it will replace all original validators. If performance is important, you should provide some additional information, at least - expected connections per second. But if you want to replace, say 10 or even 100 messages, you will not see any difference between suggested approaches.
HyperlinkedModelSerializer behaves like ModelSerializer, but it represents hyperlinks instead of ids. Just a matter of style.
I always place mixins into, well, mixins module (like views, models and so on).
Yes, you can. The SetCustomErrorMessagesMixin does not depend on the serializer type
Seems like I forgot to add required imports, sorry. I've updated the answer, please, take look on it.
|
1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.