Opened 10 years ago

Closed 10 years ago

Last modified 10 years ago

#21519 closed New feature (wontfix)

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

Reported by: Aaron Adams Owned by: nobody
Component: Database layer (models, ORM) Version: dev
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

Description

(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 by Aymeric Augustin, 10 years ago

Resolution: wontfix
Status: newclosed

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 by Aaron Adams, 10 years ago

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