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


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

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

这很容易。按照示例代码进行操作

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

我们将以下代码放入 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 在任何之后使用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/

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


1065
2018-06-04 02:08


起源

我们在谈论什么版本的Django? 1.7在apploading方面进行了重大改革。 - RickyA
我正在使用1.6。我不知道1.7中的任何变化。我会检查一下。 - mjandrews
仍处于测试阶段:( - RickyA
这里有问题吗? - 否则我很想说 The answer is Yes并将其标记为关闭。 - Burhan Khalid
有一个问题,我已经编辑了我的原始帖子,希望尽可能清楚。 - mjandrews


答案:


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

9
2018-02-15 16:05



makemigrations 动态调用时命令错误。您的迁移应该在开发期间创建,并随动态应用程序一起提供。所以最好只打电话 migrate。 - nerdoc


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

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

基本的解决方案很简单,我发现它 一个小小的博客

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

3
2018-06-05 00:35



这不再适用于Django 2.0。 django.db.models.loading 不再可用。 - nerdoc