Динамическая загрузка django-приложений во время выполнения
Можно ли динамически загружать приложения django во время выполнения? Обычно приложения загружаются при инициализации с использованием кортежа INSTALLED_APPS в settings.py. Однако возможно ли загрузить дополнительные приложения во время выполнения? Я сталкиваюсь с этой проблемой в разных ситуациях. Например, одна ситуация возникает во время тестирования, когда я хочу динамически загружать или выгружать приложения.
Чтобы сделать проблему более конкретной, представьте, что у меня есть каталог с названием apps
куда я помещаю свои приложения, и я хотел бы автоматически установить любое новое приложение, которое появляется там, без ручного редактирования settings.py.
Это достаточно просто. Следуя примеру кода в
Django: динамически добавлять приложения в виде плагина, автоматически создавать URL-адреса и другие параметры
мы поместили следующий код в settings.py
чтобы можно было перебирать имена всех подкаталогов в каталоге приложения и увеличивать кортеж INSTALLED_APPS
в settings.py
следующим образом:
APPS_DIR = '/path_to/apps/'
for item in os.listdir(APPS_DIR):
if os.path.isdir(os.path.join(APPS_DIR, item)):
app_name = 'apps.%s' % item
if app_name not in INSTALLED_APPS:
INSTALLED_APPS += (app_name, )
После этого, если бы я был в оболочке Django, я мог бы что-то вроде
from django.conf import settings
и приложения будут перечислены в settings.INSTALLED_APPS
. И если бы я сделал
from django.core import management
management.call_command('syncdb', interactive=False)
это создаст необходимые таблицы БД для приложений.
Однако, если бы я сейчас добавил несколько приложений в каталог apps/
без перезапуска, они не были бы перечислены в settings.INSTALLED_APPS, и поэтому последующий вызов syncdb
не имел бы никакого эффекта.
Я хотел бы знать, есть ли что-то, что я мог бы сделать - без перезапуска - чтобы перезагрузить настройки и загрузить/установить новые приложения.
Я пытался напрямую импортировать мои settings.py
, т.е. из настроек импорта myproject
и затем reload
эти settings
с помощью встроенного Python после любого изменения каталога app
. Несмотря на то, что settings.INSTALLED_APPS теперь изменяется, чтобы включить новые приложения, это в конечном итоге не имеет значения. Например,
from django.db import models
models.get_apps()
показывает только оригинальные приложения в apps
а не недавно добавленные и аналогично
management.call_command('syncdb', interactive=False)
не будет видеть недавно добавленные приложения.
Как я уже говорил выше, я размышлял об этой ситуации, особенно в контексте тестирования, где я динамически добавлял или удалял приложения.
Ps. Я работаю с django 1.6, но по совету @RickyA я вижу, что в django есть некоторые существенные изменения в обработке приложений в 1.7
https://docs.djangoproject.com/en/1.7/ref/applications/
Я все еще не уверен, что это может означать для проблемы, с которой я сталкиваюсь.
Ответы
Ответ 1
Чтобы ответить на мой собственный вопрос...
Хотя у меня нет полностью общего решения этой проблемы, у меня есть тот, который достаточен для динамической загрузки приложений во время тестирования.
Базовое решение простое, и я нашел его в маленьком блоке с бикс-блогами.
Продолжая мой пример выше, если я был в оболочке django и хотел добавить и загрузить некоторые новые приложения, которые были добавлены в мой каталог apps
, я мог бы сделать
import os
from django.conf import settings
from django.db.models import loading
from django.core import management
APPS_DIR = '/path_to/apps/'
for item in os.listdir(APPS_DIR):
if os.path.isdir(os.path.join(APPS_DIR, item)):
app_name = 'apps.%s' % item
if app_name not in settings.INSTALLED_APPS:
settings.INSTALLED_APPS += (app_name, )
а затем
loading.cache.loaded = False
management.call_command('syncdb', interactive=False)
Ответ 2
Обновление для Django 1.8 о том, как загрузить приложение, которое еще не загружено
from collections import OrderedDict
from django.apps import apps
from django.conf import settings
from django.core import management
new_app_name = "my_new_app"
settings.INSTALLED_APPS += (new_app_name, )
# To load the new app let reset app_configs, the dictionary
# with the configuration of loaded apps
apps.app_configs = OrderedDict()
# set ready to false so that populate will work
apps.ready = False
# re-initialize them all; is there a way to add just one without reloading them all?
apps.populate(settings.INSTALLED_APPS)
# now I can generate the migrations for the new app
management.call_command('makemigrations', new_app_name, interactive=False)
# and migrate it
management.call_command('migrate', new_app_name, interactive=False)
Ответ 3
Спасибо за информацию. Мне нужно сделать сам пользовательский интерфейс (браузер). Мне нужно получить valur от пользователя и создать приложение на основе этого. Возможно ли это в этом случае?
Ответ 4
Да! Все (или почти все) в Python возможно. Вы должны использовать os.walk(), чтобы получить все папки, подпапки и файлы в пути ваших приложений, чтобы получить все ваши приложения, включая вложенные.
def get_installed_apps():
from os import walk, chdir, getcwd
previous_path = getcwd()
APPS_ROOT_PATH = '/my/project/apps/folder'
chdir(APPS_ROOT_PATH)
for root, directories, files in walk(top=getcwd(), topdown=False):
for file in files:
if 'apps.py' in file:
print(f"{root.replace(BASE_DIR + '/', '').replace('/', '.')}.apps")
chdir(previous_path)
return