Opened 5 months ago

Closed 5 months ago

Last modified 5 months ago

#35578 closed Uncategorized (invalid)

Customizing `username_validator` in custom User model extending `django.contrib.auth.models.AbstractUser` doesn't work

Reported by: Oscar Rovira Owned by:
Component: contrib.auth Version: 5.0
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Hi there!
I'm sorry if that is the expected behaviour of what I'm going to report, but looking at the implementation, it should work as I expected.
I have my own authentication User model, extending from AbstracUser. Looking at the code of AbstractUser, one may think that defining the class prop username_validator, the validation of the username at all the levels (createsuperuser command, Admin create user form, User.objects.create_user, ...) should use the new validator provided in the customized class. It is not working like that. In order to make it work, one has to re-define also the username field in the customized model.

So, I have the following:

@deconstructible
class CustomUnicodeUsernameValidator(validators.RegexValidator):
    """Overrides the default username validator from Django Abstract
    User to avoid usernames with characters '@', '+' or '.'
    """
    regex = r"^[\w-]+\Z"
    message = _(
        "Enter a valid username. This value may contain only letters, "
        "numbers, and -/_ characters."
    )
    flags = 0

...
class User(AbstractUser):
    """Main app user, extending default Django users."""

    # overriding the one defined in AbstractUser
    username_validator = CustomUnicodeUsernameValidator() # type: ignore
    
    # Other custom fields
    ...

and it does not work, the custom username_validator is not being used.

However, if I redefine the username field, including the validators=[username_validator] attribute, it works as expected.
As I said, maybe it is the expected behaviour, and it's just a lack of documentation.

Thanks!

Change History (2)

comment:1 by Natalia Bidart, 5 months ago

Resolution: invalid
Status: newclosed

Hello Oscar, thank you for your report. What you are describing is explained by how Python class definition works, this is not about Django. See this simpler example:

In [1]: class Abstract:
    ...:     validator = "Foo"
    ...:     username = f"The {validator} username"

In [2]: Abstract().username
Out[2]: 'The Foo username'

In [3]: class MyClass(Abstract):
    ...:     validator = "Bar"

In [4]: MyClass().username
Out[4]: 'The Foo username'

In this example, the class attribute username is defined (and "calculated") at class definition time and is not something "dynamic" that you can expect it to be "recalculated" in children, if you don't override it.

Having said the above, please also note that the user customization docs indicate that if you wish to customize the User model, ideally you would extend AbstractBaseUser. Specifically AbstractUser should be used when adding more fiels, not changing existing fields behavior:

If you’re entirely happy with Django’s User model, but you want to add some additional profile information, you could subclass django.contrib.auth.models.AbstractUser and add your custom profile fields, although we’d recommend a separate model as described in Specifying a custom user model.

So this means that if you are extending AbstractUser, the default username validation should suffice for your case. If it does not suffice, then perhaps you should consider extending AbstractBaseUser and building your own validator and custom username field as your business logic requires.

Because of the above, this report seems better suited to be a support request. The best place to get more answers to your issue is using any of the user support channels from this link. Since the goal of this issue tracker is to track issues about Django itself, and your issue seems, at first, to be located in your custom code, I'll be closing this ticket as invalid following the ticket triaging process.

comment:2 by Oscar Rovira, 5 months ago

Thanks for the clarification Natalia. I guess that I misunderstood the point about "if you want to add some additional profile information (fields)", because I want it, keeping the username field and just modifying the validator. Now everything makes sense.

Note: See TracTickets for help on using tickets.
Back to Top