在运行时动态加载django应用程序

时间:2014-06-04 02:08:49

标签: python django django-settings django-apps

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

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

这很容易。按照

中的示例代码

Django: Dynamically add apps as plugin, building urls and other settings automatically

我们在settings.py中放入以下代码,可以遍历app目录中所有子目录的名称,并在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 shell中,我可以像

那样
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在任何app目录更改后使用python内置版。虽然现在更改了settings.INSTALLED_APPS以包含新添加的应用,但这最终没有任何区别。例如,

from django.db import models
models.get_apps()

仅显示apps中的原始应用,而不是新添加的应用,同样

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

不会看到新添加的应用。

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

聚苯乙烯。我使用django 1.6,但根据@RickyA的建议,我发现django对1.7中的应用程序处理有一些实质性的改变

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

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

5 个答案:

答案 0 :(得分:11)

关于如何加载尚未加载的应用程序的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)

答案 1 :(得分:3)

回答我自己的问题......

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

基本解决方案很简单,我在a wee little bixly blog找到了它。

继续上面的示例,如果我在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)

答案 2 :(得分:1)

在Django 2.2中,这项工作对我来说

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="translator">
  <input type="text" id="input" value="Obi wan">
  <button id="button">Convert to Yoda-Speak!</button>
  <textarea id="result"> Asdasdas.  Herh herh herh.</textarea>
</div>

答案 3 :(得分:0)

是的! Python中的任何内容(或几乎所有内容)都是可能的。您应该使用os.walk()来获取应用程序路径中的所有文件夹,子文件夹和文件,以便获取包括嵌套应用程序在内的所有应用程序。

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())

答案 4 :(得分:0)

在Django> = 1.7(以及当前的3.0)中,可以动态加载卸载应用在测试中 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中,启动时的动态配置很容易。启动后不建议在生产中进行动态加载和卸载,即使最终可以运行。