Opened 8 years ago
Last modified 7 weeks ago
#28594 new Bug
Value error on related user name during save of user model
Reported by: | Axel Rau | Owned by: | |
---|---|---|---|
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 (7)
comment:1 by , 8 years ago
Description: | modified (diff) |
---|
comment:2 by , 8 years ago
The attached trivial patch 28594.diff works for me with Django 1.11.4 on Python 3.5.3
comment:3 by , 8 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 , 8 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.
comment:5 by , 7 weeks ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
I will do some research here, but it seems done or old or unclosed ...
comment:6 by , 7 weeks ago
Owner: | removed |
---|---|
Status: | assigned → new |
Hi, and thanks for the opportunity to look into this ticket.
It seems that the issue has already been addressed in some form (via documentation updates), and it may have just been left open by oversight. In any case, the problem is no longer present in supported Django versions — as of 3.0, using a ForeignKey for USERNAME_FIELD is unsupported.
Given that, I would suggest closing this ticket as either resolved or invalid, unless there’s still something outstanding I may have missed.
Can you propose a patch for Django?