Opened 3 years ago

Closed 3 years ago

Last modified 3 years ago

#33272 closed Uncategorized (invalid)

Non-installed model error isn't raised if the module is in the submodule of an installed app

Reported by: David Seddon Owned by: nobody
Component: Database layer (models, ORM) Version: 3.2
Severity: Normal Keywords:
Cc: James Owen Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Imagine a Django project with two apps:

  • foo - included in INSTALLED_APPS.
  • foo.bar - not included in INSTALLED_APPS.

If a model from foo.bar is imported, I would expect Django to error along these lines:

RuntimeError: Model class foo.bar.SomeModel doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.

However, instead it imports successfully. Only when the application queries the database will it run into an error, as the table for the model will never have been created.

The root cause is get_containing_app_config, which just looks for the innermost installed application that is a parent of the model.

I wonder if there is a way to detect that the model isn't actually part of an installed app?

Change History (5)

comment:1 by James Owen, 3 years ago

Cc: James Owen added

comment:2 by Mariusz Felisiak, 3 years ago

Resolution: invalid
Status: newclosed

Thanks for this ticket, however it works as documented:

"Once you have defined your models, you need to tell Django you’re going to use those models. Do this by editing your settings file and changing the INSTALLED_APPS setting to add the name of the module that contains your models.py."

Moreover, it should be possible to define models in a different module however you have to manually propagate changes to your database schema. I don't see any reason to raise an error in such case. If you're having trouble understanding how Django works, see TicketClosingReasons/UseSupportChannels for ways to get help.

comment:3 by David Seddon, 3 years ago

Thanks for your reply and sorry if I haven't explained the issue well enough. I'm going to give it another try.

(Context: I've been a full time Django dev for over a decade so I'm not coming to this as a newbie.)

My argument is that Django has an inconsistent picture of what models belong to an app.

In the above example, let's imagine we run manage.py makemigrations foo. Django will not pick up anything from foo/bar/models.py, because they are not imported from foo/models.py. In that version of things, Django (rightly I think) does not view those models as part of the app.

However, if, elsewhere, we import a model from foo/bar/models.py, the get_containing_app_config method will treat the model as in foo application, even though it wasn't (and shouldn't have been) registered.

In my opinion the Model class x doesn't declare an explicit app_label... exception should be raised whenever a model is imported that wasn't registered in the course of Django bootstrapping. It shouldn't matter that that module happens to be in a subpackage of an installed app - that seems to me to be too much of a blunt instrument.

This behaviour causes us problems in our Django application, because we have apps that are only sometimes installed, that are submodules of other installed apps.

comment:4 by Carlton Gibson, 3 years ago

Hey David.

Perhaps you could propose a change that addresses your issue? When thinking about this yesterday, one consideration is that folks may be organising their models as a package, so that would need to be correctly handled. These things are often easier to consider with a test case and change in hand, especially if small.

comment:5 by David Seddon, 3 years ago

Hi Carlton,

I've looked at this in a bit more detail.

From what I understand, get_containing_app_config is called to determine the containing app the first time a model is imported. If this happens during the bootstrap process (via models autodiscovery), then all well and good: the model gets registered as part of its containing app.

The problem comes if an unregistered model class is inadvertently imported later as part of some runtime process. In this case it will go through the same registration process (but later than it should do), giving an inconsistent picture of which models are registered .We don't really want a model's registration to be triggered, say, by some incoming web request - or even just because running the webserver involves a larger import graph than running manage.py makemigrations.

Ideally, the model registration process should be able to tell whether it's being called late. Might one option be for the model registration code to insist on apps.models_ready being False before registering a model? (If it's True, it could raise a RuntimeError)?

I don't believe this would get in the way of organizing models as packages (that's something we do a lot) as the models would still be registered during the bootstrap process.

Note: See TracTickets for help on using tickets.
Back to Top