Объедините несколько команд "shell" в разных приложениях, используя "get_auto_imports`?
В Django 5.2 появилась возможность настраивать команду управления shell, переопределяя метод get_auto_imports() в подклассе команд управления (см. Примечание о выпуске или эта страница документа).
Это хорошая функция, и она хорошо работает для одного приложения, но у меня возникают проблемы при попытке масштабировать ее для нескольких приложений.
Например, в 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",
]
И в 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",
]
Проблема в том, что Django использует только одно из них — какое бы приложение ни было первым в INSTALLED_APPS. Похоже, это сделано специально, поскольку Django использует только первую подходящую команду управления, которую он находит.
Есть ли простой способ объединить автоматический импорт из нескольких приложений или распространить команду shell на все приложения без необходимости вручную централизовать все в одном месте?
Я ищу универсальное и масштабируемое решение, которое позволяет каждому приложению вносить свой собственный импорт в оболочку, в идеале по-прежнему используя get_auto_imports(), чтобы логика оставалась чистой и инкапсулированной для каждого приложения.
Я не думаю, что вам нужно определять несколько команд оболочки, вы, вероятно, можете позволить каждому приложению указывать, что должно быть автоматически импортировано.
Итак:
# app_1/apps.py
from django.apps import AppConfig
class App1Config(AppConfig):
# ...
shell_auto_imports = ('app1.module1',)
и:
# app_2/apps.py
from django.apps import AppConfig
class App2Config(AppConfig):
# ...
shell_auto_imports = ('app2.module2',)
и работать с одним shell-скриптом:
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
]
Да! Использование shell_auto_imports в качестве пользовательского атрибута в AppConfig каждого приложения - это просто и элегантно.
Для тех, кто ищет альтернативу, которая отделяет объявления импорта от AppConfig, я также протестировал другой подход:
Каждое приложение может определять небольшой shell_imports.py модуль, который объявляет свои импортные данные в списке AUTO_IMPORTS, а затем централизованная команда shell может динамически собирать их.
Пример:
В app1/shell_imports.py:
AUTO_IMPORTS = ["app1.models.ModelOne", ] # import ModelOne class directly
# or use "app1.models" to import the entire module
В app2/shell_imports.py:
AUTO_IMPORTS = ["app2.models.ModelTwo", ]
Команда центральной оболочки (например, в core/management/commands/shell.py):
from django.core.management.commands.shell import Command as BaseShellCommand
from django.apps import apps
class Command(BaseShellCommand):
def get_auto_imports(self):
imports = super().get_auto_imports()
for app_config in apps.get_app_configs():
try:
module = __import__(f"{app_config.name}.shell_imports", fromlist=["AUTO_IMPORTS"])
imports.extend(getattr(module, "AUTO_IMPORTS", []))
except ModuleNotFoundError:
pass
print("Final combined imports:", imports)
return imports
Этот вариант хорошо работает, если вы предпочитаете не использовать логику импорта в метаданных apps.py. Оба метода эффективно решают проблему — это всего лишь вопрос организационного стиля и предпочтений проекта.