python-3.xdjangoshell

Combine multiple `shell` commands across apps using `get_auto_imports`?


Django 5.2 introduced the ability to customize the shell management command by overriding the get_auto_imports() method in a management command subclass (see the release note or this page of the doc).

That's a nice feature and it works well for a single app, but I'm running into trouble trying to make it scale across multiple apps.

For instance, in app1/management/commands/shell.py:

from django.core.management.commands import shell

class Command(shell.Command):
    def get_auto_imports(self) -> list[str]:
        return [
            *super().get_auto_imports(),
            "app1.module1",
        ]

And in app2/management/commands/shell.py:

from django.core.management.commands import shell

class Command(shell.Command):
    def get_auto_imports(self) -> list[str]:
        return [
            *super().get_auto_imports(),
            "app2.module2",
        ]

The issue is that only one of these is picked up by Django — whichever app comes first in INSTALLED_APPS. This seems to be by design, as Django only uses the first matching management command it finds.

Is there a clean way to combine auto imports from multiple apps or extend the shell command across apps without having to manually centralize everything in one location?

I’m looking for a generic and scalable solution that allows each app to contribute its own imports to the shell, ideally still using get_auto_imports() so the logic stays clean and encapsulated per app.


Solution

  • I don't think you need to define multiple shell commands, you can probably let every app say what should be autoimported.

    So:

    # app1/apps.py
    
    from django.apps import AppConfig
    
    
    class App1Config(AppConfig):
        # …
        shell_auto_imports = ('app1.module1',)

    and:

    # app2/apps.py
    
    from django.apps import AppConfig
    
    
    class App2Config(AppConfig):
        # …
        shell_auto_imports = ('app2.module2',)

    and work with one shell script:

    from django.apps import apps
    from django.core.management.commands import shell
    
    
    class Command(shell.Command):
        def get_auto_imports(self) -> list[str]:
            extras = [
                item
                for config in apps.get_app_configs()
                for item in getattr(config, 'shell_auto_imports', ())
            ]
            return [*super().get_auto_imports(), *extras]