Django启动代码应放在哪里?

56

我希望在服务器启动时(开发和生产环境均适用)执行以下代码:

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

将它放在 settings.py 中不起作用,因为需要先加载设置。

将它们放在视图中并从外部访问该视图也行不通,因为有些中间件使用数据库,这些中间件会失败并且不允许我访问该视图。

将它们放在中间件中会起作用,但每次访问应用程序时都会调用它。一种可能的解决方案是创建一个中间件来完成所有工作,然后从 MIDDLEWARE_CLASSES 中删除自身,以便不再调用它。我能否在不进行太多猴子补丁的情况下实现这一点?

6个回答

61
编写中间件,在__init__中实现此功能,然后从__init__引发django.core.exceptions.MiddlewareNotUsed,Django将删除所有请求的中间件 :). 顺便说一下,__init__在启动时被调用,而不是在第一个请求时被调用,因此不会阻塞您的第一个用户。
有关添加启动信号的讨论,但这不会很快可用(例如,发送此信号的时间是个主要问题)
相关票证:https://code.djangoproject.com/ticket/13024 更新:Django 1.7包括对此的支持。 (文档,由票证链接)

2
根据https://dev59.com/f2w15IYBdhLWcg3wJ4is的说法,这可能会有问题。建议将启动函数放在urls.py中。 - Michael
2
中间件和urls.py文件都不会运行管理命令的启动代码。有更好的选择吗? - Andrei
我仍然收到多个启动调用(打印随机字符串,多次打印)。 - krizajb
2
@krizajB:请注意,这个答案是基于Django 1.0或1.1编写的,目前有更好的解决方案,甚至在Django 1.4中,这个解决方案可能已经不再有效。我会使用更多最新技术更新答案,但是我最近没有进行过太多的Django开发,如果有人知道更好的现代技术,请随意添加。 - KillianDS
2
这在 Python 1.4 中似乎无法工作...有什么建议/解决方法吗? - rui
显示剩余5条评论

11
在Django 1.7+中,如果您想运行启动代码并且:
1.避免在迁移、makemigrations、shell会话等中运行它
2.避免多次运行
一个解决方案是:
文件:myapp/apps.py
from django.apps import AppConfig

def startup():
    # startup code goes here

class MyAppConfig(AppConfig):
    name = 'myapp'
    verbose_name = "My Application"
    def ready(self):
        import os
        if os.environ.get('RUN_MAIN'):
            startup()

文件:myapp/__init__.py

default_app_config = 'myapp.apps.MyAppConfig'

这篇文章使用了@Pykler和@bdoering提供的建议。


谁将设置环境变量RUN_MAIN? - binithb
@binithb 显然 Django 在运行时执行 - Joseph Bani

4
如果您同时使用Apache/mod_wsgi,请使用以下WSGI脚本文件:http://blog.dscpl.com.au/2010/03/improved-wsgi-script-for-use-with.html。在启用语言翻译后添加所需内容即可。
import sys

sys.path.insert(0, '/usr/local/django/mysite')

import settings

import django.core.management
django.core.management.setup_environ(settings)
utility = django.core.management.ManagementUtility()
command = utility.fetch_command('runserver')

command.validate()

import django.conf
import django.utils

django.utils.translation.activate(django.conf.settings.LANGUAGE_CODE)

# Your line here.
django.core.management.call_command('syncdb', interactive=False)

import django.core.handlers.wsgi

application = django.core.handlers.wsgi.WSGIHandler()

这看起来有些不同,因为我使用了twod.wsgi。此外,我也想在开发服务器上完成这个任务。我不会像使用Apache那样底层操作。我的想法是,我可能不希望将SSH访问权限授予应用程序开发人员,但可以通过将其放入启动代码中来让他使用Django管理员。 - Attila O.

3

1
这是一个糟糕的选择,因为它依赖于服务器启动周围的包装脚本。如果有人忘记使用脚本启动服务器怎么办? - KillianDS
我同意@KillianDS的观点。不过这仍然是一种可能性。但是你需要使用你的包装器作为服务器的入口点,而服务器可以是任何平台,如mod_wsgi、app engine、tornado等。 - Attila O.

1
如果您使用mod_wsgi,可以将其放在wsgi启动应用程序中。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接