Hello,
Since Django 1.7, `get_user_model()` cannot be called at import time. Django
contains a lot of methods that start with `UserModel = get_user_model()` while
it would be more natural to declare it as a global variable in the module. This
trips up users and the reason why it’s forbidden isn’t obvious.
It's the consequence of two design decisions (which I’m responsible for) in the
app-loading refactor.
1. Before Django 1.7, the sequence in which models were loaded could depend on
which views were hit in what order after a WSGI process started. That allowed
interesting failures mode. For example, a project could randomly fail to load,
only in production, because of a circular import error. Throw multithreading
into the mix and it could get really exciting. To solve this, the new
app-loading system added an explicit `django.setup()` step that imports apps
and models in a deterministic order and doesn’t let code interact with the app
registry until it’s fully loaded.
2. Django must ensure that relationships between models are set up correctly.
Specifically it must guarantee that reverse accessors are added to each model
targeted by a foreign key. This guarantee can only be provided once all other
models have been imported. There’s been a long string of bugs in this area
around 2008 (if memory serves). The new app-loading system took this
requirement into account at the design stage and, thanks to the explicit setup,
is structurally more robust against such bugs.
Because of 2. apps.get_models() raises an exception before all models are
imported rather than return an incomplete model. Because of 1. Django gives
very little control on what happens up to this point.
However, in many cases, it doesn’t matter whether the model is fully
constructed or not. In the use case I described in my introduction, the code
only needs a reference to the model class at import time; that reference won’t
be used until run time, after the app-loading process has completed.
(Performing SQL queries through the ORM at import time doesn’t work anyway.)
For this reason, I think it would make sense to add an API similar to
`apps.get_models()`, but that would work while app-loading is still in
progress. It would make no guarantees about the functionality of the class it
returns until app-loading has completed.
I implemented a proof of concept here:
https://github.com/django/django/pull/6547.
I would appreciate feedback on the following questions:
1. Have you been missing this feature? If it’s needed outside of Django, I must
make it a public API.
2. Is it reasonable to expose it as a public API? There’s a risk that
StackOverflow will consider it as superior to get_models() because it’s more
lax; they'll assume that I put checks in get_models() simply because I like
messing with users.
3. Would you prefer adding a kwarg to get_models e.g.
apps.get_models(settings.AUTH_USER_MODEL, unsafe=True) or adding a new method
like the PR currently does? I’m thinking the former is more user friendly as
the functionality is very similar.
Thanks,
--
Aymeric.
--
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 post to this group, send email to django-developers@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-developers/67450ED7-88CD-49CE-9B63-180EDC033D55%40polytechnique.org.
For more options, visit https://groups.google.com/d/optout.