AppConfig subclass-subclass ignored

61 views
Skip to first unread message

Christian González

unread,
Feb 5, 2022, 10:39:55 AM2/5/22
to django-developers

Hi Django devs,

A time ago I asked to drop the default_app_config in apps.py or let Django create proper files itself/change all docs to reflect that.

You decided to let Django  search for a AppConfig subclass and load it automatically, if only one is available, or take the one with default=True.

That's fine so far.

I just had a hard time debugging my app with Django4, as in a routine I check for a "PluginMeta" attr in all of my apps, and treat them differently.

I decided to make all my AppConfigs inherit from a special parent AppConfig ("MeduxPluginAppConfig") that provides a few functions like initialize, and others - like pretix does it with its plugin system.

It did not work - no "special" methods and attrs of my plugins where found. I then saw that Django ignored my subclasses of MeduxPluginAppConfig, and just took "AppConfig" instead, like the docs say. But according to the docs, Django should not do that, if there is only one subclass of AppConfig available in apps.py.

I then started debugging Django, and saw that in django.apps.config.py there is the search for the configs:

app_configs = [
    (name, candidate)
    for name, candidate in inspect.getmembers(mod, inspect.isclass)
    if (
        issubclass(candidate, cls) and
        candidate is not cls and
        getattr(candidate, 'default', True)
    )
]

especially `inspect.getmembers` here returns a few "candidates", namely all classes that are found in that apps.py **including imported classes**.

As I have a

from .api import MeduxPluginAppConfig

class CommonConfig(MeduxPluginAppConfig):
    ...

, Django thinks that MeduxPluginAppConfig is also a candidate (because it also inherits from AppConfig). Now, there are "2" candidates in this file (for Django), and noone has a default attr. So it ignores them and takes the legacy AppConfig instead.

IMHO this is clearly a bug, unless you tell me that this is intended behaviour, and I should only DIRECTLY inherit AppConfig, and make no further children classes from that one.

Disclaimer: Yes, I know that I am somehow polluting the AppConfig namespace with attrs and methods that could clash with the ones you will add in far future... Pretix does that too, and honestly, there is hardly a good method, except creating  that methods in another model and using AppConfigs only as proxy with a pointer to that model...

I kindly ask for your opinion here.

Christian


-- 
Dr. Christian González
https://nerdocs.at

James Bennett

unread,
Feb 7, 2022, 1:07:13 AM2/7/22
to django-d...@googlegroups.com
On Sat, Feb 5, 2022 at 7:39 AM Christian González
<christian...@nerdocs.at> wrote:
> Disclaimer: Yes, I know that I am somehow polluting the AppConfig namespace with attrs and methods that could clash with the ones you will add in far future... Pretix does that too, and honestly, there is hardly a good method, except creating that methods in another model and using AppConfigs only as proxy with a pointer to that model...

I think this kind of gets at the core of the issue -- the namespace --
but not quite the right one, since the real issue is what you've put
into the module-level namespace of your apps.py, not the attributes
you're setting on your AppConfig.

So, Django *could* do some trickery to try to figure out how many
AppConfig subclasses in the module-level namespace were actually
defined in that specific 'apps' module, and decide that if there's
only one then that's the default, ignoring any that happen to be there
as a result of imports from other modules.

But then someone would write an email to this list saying that their
workflow involves a single AppConfig class that defines all the things
they want, and is just imported into apps.py, and why is Django no
longer respecting that? After all, it's an AppConfig subclass and it's
in the namespace of their apps.py, and there are no others visible
there, and Django's documentation clearly states that they shouldn't
have to do anything else!

So there's not really a "works for absolutely every use case" solution
here. Personally, I think that Django currently is doing the best
thing it can (given that there is no perfect thing available to do),
and that probably Django's behavior should not change. This means that
for your specific case you'll need to set default=True on the
AppConfig you want Django to use, but the documentation already was
telling you that :)
Reply all
Reply to author
Forward
0 new messages