如何在 console_script 中调用 django.setup()?

How to call django.setup() in console_script?

当前的 django 文档告诉我:

django.setup() may only be called once.

Therefore, avoid putting reusable application logic in standalone scripts so that you have to import from the script elsewhere in your application. If you can’t avoid that, put the call to django.setup() inside an if block:

if __name__ == '__main__':
    import django
    django.setup()

来源:Calling django.setup() is required for “standalone” Django usage

我正在使用 setup.py 中的入口点。这样我就没有__name__ == '__main__'.

问题

如果使用 console_scripts,如何确保 django.setup() 只被调用一次?

我应该把django.setup()放在哪里?

背景

我遇到的实际错误:Django 挂起。原因如下:https://code.djangoproject.com/ticket/27176

我想将我的应用程序移植到当前的 django 版本。更改为管理命令不是一个选项,因为其他(第三方应用程序)依赖于我的控制台脚本的存在。

有同样的问题(或类似的问题)。我通过这样做解决了它:

[警告:肮脏的解决方案]

if not hasattr(django, 'apps'):
    django.setup()

这样即使导入多次也只调用一次

这里 https://docs.djangoproject.com/en/1.10/_modules/django/#setup 我们可以看到 django.setup 实际做了什么。

Configure the settings (this happens as a side effect of accessing the first setting), configure logging and populate the app registry. Set the thread-local urlresolvers script prefix if set_prefix is True.

所以基本上为了确保设置已经完成,我们可以检查应用是否准备就绪以及设置是否已配置

from django.apps import apps
from django.conf import settings

if not apps.ready and not settings.configured:
    django.setup()

因为我喜欢无条件代码,所以我使用了这个解决方案。就像在 django 文档中一样,但是避免了丑陋的 if __name__ == '__pain__'

文件代码:

# utils/do_good_stuff.py
# This file contains the code
# No `django.setup()` needed
# This code can be used by web apps and console scripts.

from otherlib import othermethod

def say_thank_you():
    ...

主要文件:

# do_good_stuff_main.py
import django
django.setup()

def say_thank_you_main():
    from myapp import do_good_stuff
    return do_good_stuff()

setup.py:

    'console_scripts': [
        'say_thank_you=myapp.do_good_stuff_main:say_thank_you_main',
    ...

这是我目前的解决方案。欢迎反馈。有什么需要改进的吗?

我在两个生产 CLI python 包中工作,并在 console_scripts 中显式调用 django.setup()

您应该注意的最重要的事情是 env 路径中的 DJANGO_SETTINGS_MODULE

您可以在 shell 脚本中设置此值,甚至可以在 python 脚本中加载默认设置。

示例如下:

# setup.py
entry_points={
    'my-cli = mypackage.cli:main'
}

.

# cli.py
import logging
from os import environ as env


if not 'DJANGO_SETTINGS_MODULE' in env:
    from mypackage import settings
    env.setdefault('DJANGO_SETTINGS_MODULE', settings.__name__)


import django
django.setup()

# this line must be after django.setup() for logging configure
logger = logging.getLogger('mypackage')

def main():
    # to get configured settings
    from django.conf import settings

    # do stuffs


if __name__ == '__main__':
    main()

在 Django 2.2.5(至少)中,设置是在导入时配置的。 django.setup() 配置它们,因为它导入它们。调用 django.setup() 两次的问题在于,您会两次填充 settings.INSTALLED_APPS 中指定的应用程序,这可能会导致各种问题。

if not apps.ready: 检查是您应该使用的所有内容:

import django

if not apps.ready:
    django.setup()

使用 if not (apps.ready or settings.configured): - 是错误的,因为设置可能已在其他地方使用导入语句配置,并且应用程序可能仍未填充,因此 django.setup() 可能不会在需要时被调用。

就是说,如果您知道您的独立入口点(例如 main.py),您可以在没有条件的情况下直接调用它。仅当存在实际 运行 脚本两次的风险时才需要条件,可能是通过将独立模块导入另一个独立模块,以便它们都调用 django.setup。多次导入一个模块只会 运行 它一次。