Search code examples
djangodjango-authentication

override password_validation messages


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?


Solution

  • 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!