I'm working on a project where we use custom `User` using `email` as a
`USERNAME_FIELD`. We allow users to have custom usernames but we don't
require them so `username` field has `null=True`.
While editing one of the staff users through the admin panel I've stumbled
upon an error when leaving the `username` field empty:
{{{
TypeError
normalize() argument 2 must be str, not None
}}}
To register `User` model to admin I'm using:
{{{
@admin.register(User)
class UserAdminView(UserAdmin):
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2'),
}),
)
}}}
The source of the problem is the fact that `unicodedata.normalize()` in
{{{
class UsernameField(forms.CharField):
def to_python(self, value):
return unicodedata.normalize('NFKC', super().to_python(value))
}}}
needs a second argument to be a string. As `super().to_python(value)` is
actually `forms.CharField.to_python()` `''` value is returned as
`self.empty_value` with defaults to `''` but for a situation where
`CharField` has `null=True`. In this case it's determined in
`django.db.models.fields.CharField.formfield()` that
{{{
if self.null and not
connection.features.interprets_empty_strings_as_nulls:
defaults['empty_value'] = None
}}}
and our `self.empty_value` becomes `None` which is in effect passed to
normalize as a second argument.
My proposition is to fix it like this:
{{{
class UsernameField(forms.CharField):
def to_python(self, value):
value = super().to_python(value)
return None if value is None else unicodedata.normalize('NFKC',
value)
}}}
and allow the field null condition to be validated on the `model` level.
If you find this fix reasonable I'd be happy to open a PR with the
suggested change.
My project is using Django 2.2 but it's the same for 3.1.
--
Ticket URL: <https://code.djangoproject.com/ticket/32396>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* owner: nobody => Łukasz Rzepa
* status: new => assigned
--
Ticket URL: <https://code.djangoproject.com/ticket/32396#comment:1>
* owner: Łukasz Rzepa => (none)
* status: assigned => new
--
Ticket URL: <https://code.djangoproject.com/ticket/32396#comment:2>
* status: new => closed
* resolution: => duplicate
Comment:
Hi, thanks for the report — nice and clear.
I'm going to close this as a duplicate of #31972. As per the docs on
[https://docs.djangoproject.com/en/3.1/topics/auth/customizing/#custom-
users-and-the-built-in-auth-forms Custom users and the built-in auth
forms] you'll need to extend or customise the forms here. (Using
[https://docs.djangoproject.com/en/3.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_form
`ModelAdmin.get_form()`] I imagine.)
--
Ticket URL: <https://code.djangoproject.com/ticket/32396#comment:3>