为Django“插件”应用设置别名
有没有什么简单的方法可以通过别名来加载一个 Django 应用?我觉得这样可以让应用更容易被插件化。
在 settings.py 文件中,有一种常用的写法:
INSTALLED_APPS = ('... ','...', )
这里的 INSTALLED_APPS
是一个包含应用名称的元组。这种方式没问题,但我不想这样明确地写出某些应用。
我想要一个叫做 app handle 的东西,比如 external_login_app
,它可以映射到 drupal_login
或 mediawiki_login
(这些由最终用户或第三方提供),而实际的自定义模块/应用的名称则通过 settings.py 中的一个字符串变量来提供。
这个可插拔的部分(比如 mediawiki_login
)必须是一个完整的应用,拥有自己的视图、模型、表单等,而 external_login_app
必须在代码的其他地方都能访问。
这里的目标是将分散的代码与这样的插件解耦。
编辑 1:这是我正在尝试的:
在 settings.py 中:
EXTERNAL_LOGIN = __import__(EXTERNAL_LOGIN_APP)
#setting name must be upper case according to
#django docs
但是我的自定义登录应用也依赖于设置文件,所以看起来我遇到了循环导入的问题,错误信息是 module external_login does not have attribute views
。这个问题似乎很棘手,因为我甚至无法在通过 __import__
语句导入的视图中使用简单的东西,比如 render_to_response
。
编辑 2:经过一段时间的尝试,我发现使用 __import__()
在 settings.py 中调用是行不通的,因为几乎不可避免地会出现循环依赖。
到目前为止,我找到的最佳方法是将 __import__()
调用放在应用的其他 .py 文件中,这些文件提供了通用的“消费者”接口——也就是调用插件函数的那个:
在 settings.py
中:正如 Michał 在他的回答中建议的那样
EXTERNAL_LOGIN_APP = 'drupal_login'
INSTALLED_APPS = (
'...',
EXTERNAL_LOGIN_APP,
)
例如,在 app/views.py
中:
from django.conf import settings
EXTERNAL_LOGIN = __import__(settings.EXTERNAL_LOGIN_APP, \
[], [], ['api','forms'])
def login_view(request, external=False):
if external:
form = EXTERNAL_LOGIN.forms.LoginForm(request.POST)
if form.is_valid():
login = form.cleaned_data['login']
password = form.cleand_data['password']
if EXTERNAL_LOGIN.api.check_password(login, password):
#maybe create local account,
#do local authentication
#set session cookies for the
#external site to synchronize logins, etc.
2 个回答
设置
LOGIN_APP_NAME = 'drupal_login' # or 'mediawiki_login', or whatever
要在settings.py文件中尽早设置,然后把LOGIN_APP_NAME
(记得不要加引号!)放到INSTALLED_APPS
里,而不是放实际应用的名字。
如果你需要更复杂的功能来决定用哪个应用,那可以考虑在INSTALLED_APPS
里放一个像external_login_app()
这样的函数调用(同样不要加引号!),然后让这个external_login_app函数根据settings.py里的某个设置,或者某个配置文件的内容等返回应该返回的东西。(编辑:Tobu的回答展示了这样的函数可能需要返回什么,以及如何使用__import__
来实现。)
不过,我不太确定这样做能在多大程度上解耦网站的各个部分——如果用户还是需要修改settings.py,那为什么不让他们直接在合适的地方填入应用的名字呢?(编辑:好吧,现在我有点明白在这里用正确的解决方案能得到什么了……我想讨论还在继续,关于如何最好地完成所需的事情。)
编辑:我在原提问者修改问题之前就发了这个,现在第二次编辑提到了把登录应用的名字移动到settings.py里的一个单独变量中,并把这个变量包含在INSTALLED_APPS
里。现在原问题的两个编辑都已经到位,我想我能更清楚地看到问题了,虽然这让我觉得现在的情况需要一个abstract_login
应用,配合后端支持'*_login'
模块来处理drupal、mediawiki等登录方案。嗯,我现在不能提供这样的东西……希望其他人能做到。如果我有更好的主意能让整个事情变得简单,我会再编辑的。
在设置里定义应用的名称(就像Michał和你编辑的那样)是个不错的做法:
EXTERNAL_LOGIN_APP = 'drupal_login'
INSTALLED_APPS = (
'...',
EXTERNAL_LOGIN_APP,
)
在设置中,或者在一个公共模块里,你还可以考虑导入这个应用:
def external_login_app():
return __import__(EXTERNAL_LOGIN_APP, …)
然后在你导入的内容之后使用它:
external_login_app = settings.external_login_app()