4

I use UserCreationForm to create new users.

from django.contrib.auth.forms import UserCreationForm

class RegistrationForm(UserCreationForm):
      class Meta:
       model = User
       fields = ['username', 'first_name', 'last_name', 'email',  'is_active']

UserCreationForm automatically adds two fields (Password1 and Password2). If the password is too short then it raises an error, telling that. Or if it is too simple or common. It is done via django.contrib.auth.password_validation.

I wonder if I can override the messages of these errors.

right now the source code for password validation is:

def validate(self, password, user=None):
    if len(password) < self.min_length:
        raise ValidationError(
            ungettext(
                "This password is too short. It must contain at least %(min_length)d character.",
                "This password is too short. It must contain at least %(min_length)d characters.",
                self.min_length
            ),
            code='password_too_short',
            params={'min_length': self.min_length},
        )

but when I try to use this code in form definition to override this error message the label changes, but error_messages remain the same:

password1 = forms.CharField(label='New Label', error_messages={'password_too_short': 'My error message for too short passwords'})

What am I doing wrong?

Philipp Chapkovski
  • 1,949
  • 3
  • 22
  • 43

4 Answers4

9

Update: Sub-classing instead of copying/pasting is probably a better solution. See gustavo's answer.

See How to use custom password validators beside the django auth password validators? for similar instructions.

I've been looking around for the same thing and I don't think there's any way to change the error message on password validators from the form level. You'll probably end up having to write your own custom validator and then including it in your AUTH_PASSWORD_VALIDATORS in settings.py (which is what I did). Here's how I did it:

1. Go to django's built-in password validators and copy the MinimumLengthValidator code. Here's the link: https://docs.djangoproject.com/en/2.0/_modules/django/contrib/auth/password_validation/#MinimumLengthValidator

2. Create a python file in the app of your choosing (I chose my base app) and give it the name of your choosing (mine is custom_validators.py) Mine looks like this:

    from django.utils.translation import ngettext  # https://docs.python.org/2/library/gettext.html#gettext.ngettext
    from django.core.exceptions import ValidationError

    # https://docs.djangoproject.com/en/2.0/_modules/django/contrib/auth/password_validation/#MinimumLengthValidator
    class MyCustomMinimumLengthValidator(object):
        def __init__(self, min_length=8):  # put default min_length here
            self.min_length = min_length

        def validate(self, password, user=None):
            if len(password) < self.min_length:
                raise ValidationError(
                    ngettext(
                        # silly, I know, but if your min length is one, put your message here
                        "This password is too short. It must contain at least %(min_length)d character.",
                        # if it's more than one (which it probably is) put your message here
                        "This password is too short. It must contain at least %(min_length)d characters.",
                        self.min_length
                    ),
                code='password_too_short',
                params={'min_length': self.min_length},
                )

        def get_help_text(self):
            return ngettext(
                # you can also change the help text to whatever you want for use in the templates (password.help_text)
                "Your password must contain at least %(min_length)d character.",
                "Your password must contain at least %(min_length)d characters.",
                self.min_length
            ) % {'min_length': self.min_length}

3. In settings.py, comment out the following lines in your AUTH_PASSWORD_VALIDATORS :

    # {
    #     'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    # },

and add the following lines:

{
    'NAME': 'base.custom_validators.MyCustomMinimumLengthValidator',
            # app_name.file_name.validator_name
},

Now, whenever you run validate() or form.is_valid() (which also runs validate()) your password will go through your new custom password validator instead of django's default one. It might take some tweaking, but you could probably go through and do this for all of django's default validators.

Hope that helps!

kimbo
  • 2,513
  • 1
  • 15
  • 24
7

I found a better solution:

It is not necessary to replace all the code of the class, it is enough to inherit the class MinimumLengthValidator and modify the definitions that are needed. In this case validate and get_help_text.

from django.contrib.auth.password_validation import MinimumLengthValidator
from django.core.exceptions import ValidationError
from django.utils.translation import ngettext

# https://docs.djangoproject.com/en/2.0/_modules/django/contrib/auth/password_validation/#MinimumLengthValidator


class MinimumLengthValidator(MinimumLengthValidator):
    # update this definition with your custom messages
    def validate(self, password, user=None):
        if len(password) < self.min_length:
            raise ValidationError(
                ngettext(
                    "This password is too short. It must contain at least %(min_length)d character.",
                    "This password is too short. It must contain at least %(min_length)d characters.",
                    self.min_length
                ),
            ),
            code='password_too_short',
            params={'min_length': self.min_length},
        )

    # update this definition with your custom messages
    def get_help_text(self):
       return ngettext(
           "Your password must contain at least %(min_length)d character.",
           "Your password must contain at least %(min_length)d characters.",
           self.min_length
       ) % {'min_length': self.min_length}

I hope this help others with this problem.

Gustavo Santamaría
  • 837
  • 1
  • 10
  • 21
  • it doesn't work for me i did every thin the same it still shows the message in django.contrib.auth.password_validation could you help me? – M.Mevlevi Feb 16 '22 at 09:44
4

I have found a better solution for those who only want to change the validation messages just inherit the class as specified by @Gustavo and replace the password validation settings variable with your own custom classed as specified

AUTH_PASSWORD_VALIDATORS = [
{
    'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
    'NAME': 'apps.user.validators.MinimumLengthValidatorCustom',
    # 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
    'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
    'NAME': 'apps.user.validators.NumericPasswordValidatorCustom',
    # 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},

]

Pankaj78691
  • 354
  • 2
  • 7
1

Also you can use Django's built-in i18n:

1. Create file <PROJECT ROOT>/locale/en/LC_MESSAGES/django.po

msgid "This password is entirely numeric."
msgstr "The password should contain both letters and numbers."

2. In settings.py add

LOCALE_PATHS = (
    os.path.join(BASE_DIR, 'locale'),
)
LANGUAGE_CODE = 'en-us'

3. Compile messages to get django.mo file

django-admin compilemessages

After that, the message "The password should contain both letters and numbers." will be displayed instead of "This password is entirely numeric.".

In this way, you can change all the standard messages.

dtatarkin
  • 1,201
  • 8
  • 6