Opened 23 months ago

Closed 23 months ago

Last modified 22 months ago

#21519 closed New feature (wontfix)

django.db.models: get_apps() includes abstract models, get_models() excludes them

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


(I'm uncertain how much of this report is bug vs. new feature; I went with the latter for now.)

From the BaseAppCache class in django.db.models.loading:

    def get_apps(self):
        Returns a list of all installed modules that contain models.

When get_apps() says "all installed modules that contain models", it really means it: the returned list includes modules containing only abstract base class models.

    def get_models(self, app_mod=None,
                   include_auto_created=False, include_deferred=False,
                   only_installed=True, include_swapped=False):
        Given a module containing models, returns a list of the models.
        Otherwise returns a list of all installed models.


get_models() doesn't give any indication that it should work any differently; yet it ignores abstract base class models, returning a list of concrete models only.

This inconsistency is very counter-intuitive:

>>> from django.db import models
>>> [{app: models.get_models(app)} for app in models.get_apps()]
[{<module 'django.contrib.admin.models'>: [<class 'django.contrib.admin.models.LogEntry'>]},
 {<module 'django.contrib.auth.models'>: [<class 'django.contrib.auth.models.Permission'>,
                                          <class 'django.contrib.auth.models.Group'>,
                                          <class 'django.contrib.auth.models.User'>]},
 {<module 'django.contrib.contenttypes.models'>: [<class 'django.contrib.contenttypes.models.ContentType'>]},
 {<module 'django.contrib.sessions.models'>: [<class 'django.contrib.sessions.models.Session'>]},
 {<module 'django.contrib.messages.models'>: []},
 {<module 'django.contrib.staticfiles.models'>: []},
 {<module 'myapp.models'>: [<class 'myapp.models.ConcreteModel'>]},
 {<module 'myapputils.models'>: []}]

get_apps() says an app contains models, but passing that app to get_models() returns an empty list.

The docs indicate that yes, an abstract base class is a model; so I think get_models() should return them, or at least should be able to return them with an include_abstract=True argument.

(The use case is graph_models in django_extensions, which outputs a Graphviz digraph of the models in an application or project. Currently it's forced to walk up the model tree to find abstract base classes, leading to some unexpected behaviours that would be much easier to resolve without this Django quirk.)

Change History (2)

comment:1 Changed 23 months ago by aaugustin

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Resolution set to wontfix
  • Status changed from new to closed

All these are private APIs; they aren't designed for general use and I have to admit that they aren't particularly well designed.

I'm not eager to make changes to private stuff that won't be used by Django, if only for the reason that these changes may be refactored away in the future. We're always looking for opportunities to remove code ;-) For this reason, my inclination is to close this ticket as wontfix.

However, there's a plan to refactor them significantly (app-loading, #3591). As part of this plan, get_apps() will return a list of AppConfig objects that should match the contents of INSTALLED_APPS, even if some applications do not have a models module at all. Therefore I believe this issue will be resolved by app-loading (if it ever lands :/).

comment:2 Changed 22 months ago by Kanuck54

Thank you for the thoughtful response, it's much appreciated! Here's hoping that there is eventually a documented method for retrieving a list of installed applications, and perhaps even the models defined within. One day!

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