Hi Carl,
Thanks for the feedback!
> On 06 Sep 2016, at 19:39, Carl Meyer <
ca...@oddbird.net> wrote:
>
> 1) A kwarg to `get_model` seems fine, but I don't like the vague FUD of
> `unsafe=True` (if it's really "not safe" we shouldn't add it / make it
> public at all). How about something more specific like
> `require_ready=False`?
Agreed. Actually I thought there must be a better name but didn’t have a
better idea and then I forgot to mention it, the email was quite long already.
> 2) I think a key question here is the nature and clarity of the problems
> that arise if you try to make use of an un-ready model class. If the
> failure mode here is the reintroduction of unpredictable heisenbugs
> where certain related fields are quietly not present because the model
> on the other end happens to not have been imported yet, I think that's a
> strong argument for refraining from introducing this API.
In Django 1.7+, I’m pretty sure the import sequence is fully deterministic for
a given version of a project.
get_wsgi_application() runs django.setup() first thing. Not user code has been
imported at that point.
django.setup() accesses settings, which can import user code but shouldn’t —
it’s super easy to create import loops if it does. In general settings don't
import much. They might be able to import models but that's pathological.
Just don't do that.
Then apps.populate() imports apps and models in the order of INSTALLED_APPS.
This is fully deterministic, even with multi-threading: the first thread that
reaches populate() takes a lock and other threads wait until that thread has
finished.
At this point all models have been imported according in a fully deterministic
sequence (assuming non-pathological code e.g. `if random() > 0.5: import foo`)
I believe the next step is to import URLs. I didn't check if that happens
immediately or when the first request is received. Some non-determinism might
happen here, especially with per-request urlconfs, but let's focus on models
and leave that question for another day :-)
The change I’m proposing doesn’t introduce random bugs. If models are
missing reverse relations, that will be deterministic.
> Is it possible
> to make it so that even the model meta introspection APIs (and of course
> also any attempt to query the db) fail quickly, loudly, and with a clear
> error message if the app registry is not yet fully populated? If so,
> then I think there's little risk to adding this API (and in fact I think
> we could even make it the default behavior of `get_model`).
If not checking for models_ready was the default for get_model(), then I’d
expect it to be the default for get_models() as well, but that doesn’t make
sense. get_models() really needs all models to be imported.
My initial patch added a new method, `import_model`, because the operation is
quite different from the app registry's perspective. Instead of just looking
up the model, we try to import it. Even though the end result feels the same,
the context isn't the same.
Also, from the "separation of concerns" angle, I'm not eager to create
coupling between the model meta API and the app registry.
So, while I get the appeal of not adding a somewhat scary kwarg, I still think
it's good to have users declare that they're treading into pre-`models_ready`
territory.
We could relax this constraint later by ignoring the require_ready argument,
at the cost of some code churn in third-party apps that started using it.
> As for whether it's needed in real use, the only feedback I can offer at
> the moment is that the import-order enforcement that kicked in with the
> removal of app-loading deprecation shims in Django 1.9 is the primary
> reason that Instagram has so far upgraded only as far as Django 1.8;
BRB finding a patch of sand to bury my head in...
> fixing the import-order issues appeared to require too much upheaval and
> would have delayed the upgrade too much. I haven't had time to look
> closely at those issues so I can't yet offer more detailed feedback on
> how much the availability of this particular API would help.
Without looking at the code base, I can’t say. It might help if you’re doing
swappable-model-style dynamic imports. It won’t help if you’re importing
models directly or indirectly from your applications’s __init__.py, which is
a more common problem.
For the record this is tracked in #24312 but that ticket neither describes the
exact problem nor possible ways to tackle it.
I think #21719 would have led there as well but it turned out to be
surprisingly hard -- in the realm of "not sure I'd get anywhere even if I
spent two weeks full-time on it”.
--
Aymeric.