#31461 closed Cleanup/optimization (wontfix)
Make it easier to customize ValidationError messages in contrib.auth.password_validation
Reported by: | Joey van Breukelen | Owned by: | Joey van Breukelen |
---|---|---|---|
Component: | contrib.auth | Version: | 3.0 |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | yes | UI/UX: | no |
Description
Goal: Make it easier to customize ValidationError messages in contrib.auth.password_validation
Problem: None of the gettext strings are defined in class variables or are wrapped by methods, making it hard change gettext in a subclass.
Suggested solution: Wrap gettext with a method so its easy to change in subclass.
Example:
class MinimumLengthValidator: # some code left out 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}, )
Suggested change:
class MinimumLengthValidator: # some code left out def get_error_message(self): return 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 ) if len(password) < self.min_length: raise ValidationError( self.get_error_message(), code='password_too_short', params={'min_length': self.min_length}, ) class CustomMinimumLengthValidator(MinimumLengthValidator): def get_error_message(self): return 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 )
This will be similar to how get_help_text works on these classes:
class MinimumLengthValidator: # code left out 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}
The problem can also be resolved by creating class variables with gettext_lazy, but this might get messy with plurals (which MinimumLengthValidator currently has) and would be a different pattern than the get_help_text method.
Change History (2)
comment:1 by , 5 years ago
Resolution: | → wontfix |
---|---|
Status: | assigned → closed |
comment:2 by , 5 years ago
Ok, makes sense to me.
Just want to note that MinimumLengthValidator (used in example) has a very short validate method. UserAttributeSimilarityValidator for example, has a more complicated one.
def validate(self, password, user=None): if not user: return for attribute_name in self.user_attributes: value = getattr(user, attribute_name, None) if not value or not isinstance(value, str): continue value_parts = re.split(r'\W+', value) + [value] for value_part in value_parts: if SequenceMatcher(a=password.lower(), b=value_part.lower()).quick_ratio() >= self.max_similarity: try: verbose_name = str(user._meta.get_field(attribute_name).verbose_name) except FieldDoesNotExist: verbose_name = attribute_name raise ValidationError( _("The password is too similar to the %(verbose_name)s."), code='password_too_similar', params={'verbose_name': verbose_name}, )
Hi. Thanks for the report.
I'm going to say no initially.
The trade off here is a whole extra method/hook in order to save very little code.
The whole method is just this:
Do, the check, raise the
ValidationError
. That's it.The "Writing your own validator" docs have it like so:
I'm not convinced custom messages are such a common need to merit making that three methods.
(Although I guess the extra hook would be internal, but still...)
I hope that makes sense.