在运行时动态加载django应用



是否可以在运行时动态加载Django应用程序?通常,应用程序在初始化时加载,使用settings.py中的INSTALLED_APPS元组。但是,是否有可能在运行时加载其他应用程序?我在不同的情况下遇到这个问题。例如,在测试过程中,当我想动态加载或卸载应用程序时,出现了一种情况。

为了使问题更具体,假设我有一个名为apps的目录,用于存放我的应用程序,我希望自动安装任何新应用程序,而无需手动编辑settings.py。

这很简单。遵循

中的示例代码

Django:动态添加应用程序作为插件,自动构建url和其他设置

我们在settings.py中放入以下代码,以便循环遍历app目录中所有子目录的名称,并在settings.py中增加INSTALLED_APPS元组,如下所示:

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 shell中,我可以这样写

from django.conf import settings

,应用程序将在settings.INSTALLED_APPS中列出。如果我做了

from django.core import management
management.call_command('syncdb', interactive=False)

将为应用程序创建必要的DB表。

然而,如果我现在要添加一些更多的应用程序到apps/目录,没有重新启动,这些将不会在设置中列出。INSTALLED_APPS,因此后续对syncdb的调用将不起作用。

我想知道的是,如果有什么我可以做-不重新启动-重新加载设置和加载/安装新的应用程序。

我已经尝试直接导入我的settings.py,即from myproject import settings

然后是reload, settings使用python内置在任何app目录更改后。尽管设置。INSTALLED_APPS现在更改为包含新添加的应用程序,这最终没有区别。例如,

from django.db import models
models.get_apps()

只显示apps中的原始应用程序,而不显示新添加的应用程序,同样

management.call_command('syncdb', interactive=False)

将看不到新添加的应用。

正如我上面所说的,我正在考虑这种情况,特别是在我动态添加或删除应用程序的测试环境中。

p。我使用的是Django 1.6,但是在@RickyA的建议下,我看到Django在1.7中对应用程序的处理有一些实质性的变化

https://docs.djangoproject.com/en/1.7/ref/applications/

我仍然不确定这对我面临的问题可能意味着什么。

更新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's 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)

对于Django 2.2,这对我来说是可行的

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, )
apps.app_configs = OrderedDict()
apps.apps_ready = apps.models_ready = apps.loading = apps.ready = False
apps.clear_cache()
apps.populate(settings.INSTALLED_APPS)
management.call_command('makemigrations', new_app_name, interactive=False)
management.call_command('migrate', new_app_name, interactive=False)

回答我自己的问题…

虽然我对这个问题没有一个完全通用的解决方案,但我确实有一个足以在测试期间动态加载应用程序的解决方案。

基本的解决方案很简单,我在一个小小的bixly博客上找到了它。

继续上面的例子,如果我在django shell中,想要添加和加载一些新的应用程序,这些应用程序被添加到我的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)

可以动态地加载卸载应用测试在Django>= 1.7(以及当前4.1)通过override_settings()装饰器:

@override_settings(INSTALLED_APPS=[...])  # added or removed some apps
class MyTest(TestCase):
    # some tests with these apps
    def test_foo(self):
        pass

从2014年9月开始就可能了,而不是在这个问题之前。

这个问题的许多其他主题也在Django>= 1.7中被django.apps解决了。在当前的Django中,启动时的动态配置很容易。启动后动态加载和卸载在生产中是不推荐的,即使它最终可以工作。

是的!Python中的任何事情(或几乎所有事情)都是可能的。你应该使用os.walk()来获取app路径下的所有文件夹、子文件夹和文件,以便获取所有的app,包括嵌套的app。

def get_installed_apps():
    from os import walk, chdir, getcwd
    previous_path = getcwd()
    master = []
    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:
                app_path = f"{root.replace(BASE_DIR + '/', '').replace('/', '.')}.apps"
                print(app_path)
                master.append(app_path)
    chdir(previous_path)
    return master

print(get_installed_apps())

相关内容

  • 没有找到相关文章

最新更新