Hi,
This discussion does not seems to attract much attention but I still
feel this is a nice improvement to have.
/Cordialement/Regards,
Christophe Henry/
—
Christophe Henry
https://christophe-henry.dev/
Dev. Python/JS/Android & devops
Timon-Ethics / Coopaname <http://www.coopaname.coop/>
------------------------------------------------------------------------
Le 08/10/2021 à 13:55, Christophe Henry a écrit :
Hello Django developpers,
Following the discussion I had with Mariusz Felisiak on PR #14913
<https://github.com/django/django/pull/14913/files/f7492ae19586e6fd76e235cbbe902b5da657423e#diff-e5abf5944f650c7b4cebaee079168b66a303f57e96c2cf441a0a5a0251e68524>,
I would like to suggest a small change in the way the createsuperuser
command processes values for foreign key fields of custom User models.
This all started as I was trying to override the createsuperuser
command for a Django project I work on. This project has a custom user
model with a FK field named `organisation`. As I was working on that,
I noticed that, if I passed an instance of the Organisation model for
the field organisation, I got a TypeError because createsuperuser
performs a field.clean on the value which tries to convert a model
into an integer. But if I pass an integer, I get a ValueError, because
later in the code, the command calls the the AbstractUser constructor
which expects an instance of Organisation for the organisation field.
It felt like a bug, so I opened #33151
<https://code.djangoproject.com/ticket/33151> and proposed to fix this
in a PR that simply wraps the input value in a model
<https://github.com/django/django/pull/14913/files#diff-008fad28cbf57009885eb4ba44f8460c9d58088af8bd9be3db2046cac37744f1R142>.
But, during the review, Mariusz Felisiak informs me that this
behaviour is documented
<https://docs.djangoproject.com/en/3.2/topics/auth/customizing/#django.contrib.auth.models.CustomUserManager>
as it is advised to always write a custom manager with the create_user
and create_superuser methods and any change here is a breaking change
that should be discussed on the developer's mailing list. Which is why
I would like to make my case here.
I would like to stress how easy it is to miss that part of the
documentation and run into this unexpected behaviour. Everywhere in
the framework, when I use the name of an FK field, an instance of the
model is expected. This is true for model constructors and every
method of the ORM. Which is why it was disturbing to me seeing
createsuperuser break this convention and use the name of the FK field
to pass the primary key.
This means, every time I will use a custom user model in combination
to REQUIRED_FIELDS, I will have to write a custom manager with
create_user and create_superuser methods that do this:
class CustomUserManager(UserManager):
def __normalize_fields(self, extra_fields: dict):
for field_name in extra_fields.keys():
field = self.model._meta.get_field(field_name)
field_value = extra_fields[field_name]
if field.many_to_one and not isinstance(
field_value, field.remote_field.model
):
field_value = (
int(field_value)
if not isinstance(field_value, int)
else field_value
)
extra_fields[field_name] =
field.remote_field.model(field_value)
def create_user(self, username, email=None, password=None,
**extra_fields):
self.__normalize_fields(extra_fields)
return super().create_user(username, email, password,
**extra_fields)
def create_superuser(self, username, email=None,
password=None, **extra_fields):
self.__normalize_fields(extra_fields)
return super().create_superuser(username, email, password,
**extra_fields)
Even though it's this only thing it does. IMHO, the createsuperuser
command could be — and should be — doing this itself. BTW, it's what's
being done in interactive mode
<https://github.com/django/django/blob/main/django/contrib/auth/management/commands/createsuperuser.py#L142>
to check the validity of the user input, even though the wrapping is
discarded immediatly after and only the raw value is transmitted. I
don't really see why the command performs a check in this situation
but not always.
--
/Cordialement/Regards,
Christophe Henry/
—
Christophe Henry
https://christophe-henry.dev/
Dev. Python/JS/Android & devops
Timon-Ethics / Coopaname <http://www.coopaname.coop/>
------------------------------------------------------------------------
--
You received this message because you are subscribed to the Google Groups "Django
developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-developers/d04106db-a23f-a7a4-7673-4589d0aabb34%40timon-ethics.fr.