On Mon, Aug 21, 2017 at 7:50 AM, Axel Rau <axel....@chaos1.de> wrote:

> 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.
>
> 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
>
>
>
<snip>


>   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 "'unp...@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
>
> Any help to resolve this appreciated,
> Axel
>


You've set your USERNAME_FIELD to a field that is an FK to another table.
I've never seen that before, but apparently the docs say it is supported.
To be fair, that's the extent of the documentation on how to utilize an FK
as the username field.

https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#django.contrib.auth.models.CustomUser.USERNAME_FIELD

It makes sense to me that this particular operation doesn't work. You're
asking (by way of the auth model definition through a ModelForm, I'm
assuming) for the string value of 'unp...@framailx.de' to be set as the
value of an FK field on the Account model (which I assume is the one that
inherits from AbstractEmailUser) for the localemail field, when in reality
you would want to set a value that matches the PK of the related row in the
Mailbox model. Django will perform this PK magic automatically if you
provide a Mailbox object as the value for Account.localemail, rather than
the email string itself (per the docs). It will not automatically convert
an email address to a Mailbox object, or determine which row should be
referenced by Account.localemail without help, as far as I know.

TL;DR; the error occurs when 'unp...@framailx.de' is set on
Account.localemail, when Account.localemail is expecting either a Mailbox
object (that has a PK), or an integer matching a PK in the Mailbox table.
It doesn't know how to translate a raw email address string to a Mailbox
object.

I suppose the 'fix' would be to retrieve a Mailbox object based on the
email address provided, and use that as the value for Account.localmail.
You should be able to implement that in a custom ModelForm as part of
clean_localemail().

If this was working under 1.9 (I'd be surprised if it was), then I would
file this as a backward-incompatibility bug with the bug tracker. It's
possible that 1.9 did do the type/field conversion using some black magic,
but with the information in your model, I don't know how it could have made
that determination.

>From a design perspective, the relation you presented seems backwards to
me. I would think the Account model would hold the canonical username, and
Mailbox would have the FK back to the Account. That design would not
require the use of a username field as an FK, simplifying everything
(authentication, forms, views, etc.). Your current design would implement
excessive SQL JOIN's to pull relatively simple data on every request.

Or, I could be completely wrong. :-D

-James

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-users+unsubscr...@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/CA%2Be%2BciVstk8eDLSkJijVaxMU8%3DXxjJ35AXZcu4yWh8M4gkxcdw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to