Opened 8 years ago
Last modified 5 months 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_FIELDnow supports
:class:~django.db.models.ForeignKey\s. Since there is no way to pass
model instances during the :djadmin:createsuperuserprompt, expect the
user to enter the value of :attr:~django.db.models.ForeignKey.to_field
value (the :attr:~django.db.models.Field.primary_keyby 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 , 5 months 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 , 5 months 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?