Opened 7 years ago
Last modified 7 years ago
#28594 new Bug
Value error on related user name during save of user model
Reported by: | Axel Rau | Owned by: | nobody |
---|---|---|---|
Component: | contrib.auth | Version: | 1.11 |
Severity: | Normal | Keywords: | Value error, user model, normalize_username |
Cc: | Triage Stage: | Accepted | |
Has patch: | yes | Needs documentation: | yes |
Needs tests: | no | Patch needs improvement: | yes |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
problem:
While upgrading from 1.9 to 1.11, ForwardManyToOneDescriptor.__set__()
tries to assign a string (description of the related instance) to the related user name field of user model.
diagnosis:
In 1.10 normalize_username()
, a class method of AbstractBaseUser
, has been introduced. This class method forces the related user name instance to a string.
Fix:
Overwriting this with a method which just returns the 'username' fixes the problem.
Details:
class AbstractEmailUser(AbstractBaseUser, PermissionsMixin, FieldlistForDetailTemplateMixin): localemail = models.OneToOneField('Mailbox', verbose_name=_('Local E-mail'), related_name='localemail', db_column='localemail', editable=('UR', 'UE', 'UL')) objects = UserManager() USERNAME_FIELD = 'localemail' REQUIRED_FIELDS = [] class Meta: abstract = True ordering = ['localemail'] def get_username(self): return getattr(self, self.USERNAME_FIELD) class Mailbox(models.Model): id = models.AutoField(primary_key=True) localpart = models.CharField(_('Localpart'), max_length=40) localdomainfk = models.ForeignKey(Localdomain, verbose_name=_('Domain'), db_column='localdomainfk', editable=('AL',)) … def __str__(self): return self.localpart+ '@'+self.localdomainfk.name Internal Server Error: /admin/erdb/account/19/change/ Traceback (most recent call last): File "...python3.5/site-packages/django/core/handlers/exception.py", line 41, in inner response = get_response(request) File "...python3.5/site-packages/django/core/handlers/base.py", line 249, in _legacy_get_response response = self._get_response(request) File "...python3.5/site-packages/django/core/handlers/base.py", line 187, in _get_response response = self.process_exception_by_middleware(e, request) File "...python3.5/site-packages/django/core/handlers/base.py", line 185, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/contextlib.py", line 30, in inner return func(*args, **kwds) File "...python3.5/site-packages/django/contrib/admin/options.py", line 551, in wrapper return self.admin_site.admin_view(view)(*args, **kwargs) File "...python3.5/site-packages/django/utils/decorators.py", line 149, in _wrapped_view response = view_func(request, *args, **kwargs) File "...python3.5/site-packages/django/views/decorators/cache.py", line 57, in _wrapped_view_func response = view_func(request, *args, **kwargs) File "...python3.5/site-packages/django/contrib/admin/sites.py", line 224, in inner return view(request, *args, **kwargs) File "...python3.5/site-packages/django/contrib/admin/options.py", line 1511, in change_view return self.changeform_view(request, object_id, form_url, extra_context) File "...python3.5/site-packages/django/utils/decorators.py", line 67, in _wrapper return bound_func(*args, **kwargs) File "...python3.5/site-packages/django/utils/decorators.py", line 149, in _wrapped_view response = view_func(request, *args, **kwargs) File "...python3.5/site-packages/django/utils/decorators.py", line 63, in bound_func return func.__get__(self, type(self))(*args2, **kwargs2) File "...python3.5/site-packages/django/contrib/admin/options.py", line 1408, in changeform_view return self._changeform_view(request, object_id, form_url, extra_context) File "...python3.5/site-packages/django/contrib/admin/options.py", line 1440, in _changeform_view if form.is_valid(): File "...python3.5/site-packages/django/forms/forms.py", line 183, in is_valid return self.is_bound and not self.errors File "...python3.5/site-packages/django/forms/forms.py", line 175, in errors self.full_clean() File "...python3.5/site-packages/django/forms/forms.py", line 386, in full_clean self._post_clean() File "...python3.5/site-packages/django/forms/models.py", line 408, in _post_clean self.instance.full_clean(exclude=exclude, validate_unique=False) File "...python3.5/site-packages/django/db/models/base.py", line 1234, in full_clean self.clean() File "...python3.5/site-packages/django/contrib/auth/base_user.py", line 77, in clean setattr(self, self.USERNAME_FIELD, self.normalize_username(self.get_username())) File "...python3.5/site-packages/django/db/models/fields/related_descriptors.py", line 216, in __set__ self.field.remote_field.model._meta.object_name, ValueError: Cannot assign "'unpriv@framailx.de'": "Account.localemail" must be a "Mailbox" instance. [21/Aug/2017 16:08:37] "POST /admin/erdb/account/19/change/ HTTP/1.1" 500 166385
Discussion:
https://www.mail-archive.com/django-users@googlegroups.com/msg178769.html
Attachments (1)
Change History (5)
comment:1 by , 7 years ago
Description: | modified (diff) |
---|
comment:2 by , 7 years ago
The attached trivial patch 28594.diff works for me with Django 1.11.4 on Python 3.5.3
comment:3 by , 7 years ago
Has patch: | set |
---|---|
Needs documentation: | set |
Patch needs improvement: | set |
Triage Stage: | Unreviewed → Accepted |
At the very least this should use six.string_types
on 1.x branches. But I think this is a documentation issue. If you change the type of the username field, then you should reimplement normalize_username or prevent it being called.
However, Django's documentation says:
:attr:
USERNAME_FIELD
now supports
:class:~django.db.models.ForeignKey
\s. Since there is no way to pass
model instances during the :djadmin:createsuperuser
prompt, expect the
user to enter the value of :attr:~django.db.models.ForeignKey.to_field
value (the :attr:~django.db.models.Field.primary_key
by default) of an
existing instance.
The current behavior of clean()
and UserManager._create_user()
does not support foreign keys as it should expect to be handed model instances and not call normalize_username
or as the patch suggests, normalize_username
should only act on strings.
comment:4 by , 7 years ago
I agree, that this is a corner case and can be prevented by a hint in the documentaion.
My proposal of the doc enhancement is attached. It replaces my original code patch.
Can you propose a patch for Django?