我正在编写一个Django中间件类,我想在启动时只执行一次,以初始化其他一些任意代码。我遵循了sdolan在这里发布的非常好的解决方案,但是“Hello”消息输出到终端两次。如。

from django.core.exceptions import MiddlewareNotUsed
from django.conf import settings

class StartupMiddleware(object):
    def __init__(self):
        print "Hello world"
        raise MiddlewareNotUsed('Startup complete')

在我的Django设置文件中,我已经在MIDDLEWARE_CLASSES列表中包含了这个类。

但是当我使用runserver运行Django并请求一个页面时,我进入了终端

Django version 1.3, using settings 'config.server'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Hello world
[22/Jul/2011 15:54:36] "GET / HTTP/1.1" 200 698
Hello world
[22/Jul/2011 15:54:36] "GET /static/css/base.css HTTP/1.1" 200 0

知道为什么"Hello world"打印了两次吗?谢谢。


当前回答

更新:Django 1.7现在有一个钩子

: myapp / app . py文件

from django.apps import AppConfig
class MyAppConfig(AppConfig):
    name = 'myapp'
    verbose_name = "My Application"
    def ready(self):
        pass # startup code here

文件:myapp / __init__ . py

default_app_config = 'myapp.apps.MyAppConfig'

对于Django < 1.7

第一个答案似乎不再工作了,urls.py在第一次请求时加载。

最近起作用的是将启动代码放在任何一个INSTALLED_APPS init.py中,例如myapp/__init__.py

def startup():
    pass # load a big thing

startup()

当使用。/manage.py runserver…这将被执行两次,但这是因为runserver有一些技巧来首先验证模型等等。正常部署,甚至当runserver自动重新加载时,这只执行一次。

其他回答

正如@Pykler所建议的,在Django 1.7+中,你应该使用他回答中解释的钩子,但是如果你想让你的函数只在run server被调用时被调用(而不是在进行迁移,migrate, shell等被调用时),并且你想避免AppRegistryNotReady异常,你必须这样做:

: myapp / app . py文件

import sys
from django.apps import AppConfig

class MyAppConfig(AppConfig):
    name = 'my_app'

    def ready(self):
        if 'runserver' not in sys.argv:
            return True
        # you must import your modules here 
        # to avoid AppRegistryNotReady exception 
        from .models import MyModel 
        # startup code here

更新:Django 1.7现在有一个钩子

: myapp / app . py文件

from django.apps import AppConfig
class MyAppConfig(AppConfig):
    name = 'myapp'
    verbose_name = "My Application"
    def ready(self):
        pass # startup code here

文件:myapp / __init__ . py

default_app_config = 'myapp.apps.MyAppConfig'

对于Django < 1.7

第一个答案似乎不再工作了,urls.py在第一次请求时加载。

最近起作用的是将启动代码放在任何一个INSTALLED_APPS init.py中,例如myapp/__init__.py

def startup():
    pass # load a big thing

startup()

当使用。/manage.py runserver…这将被执行两次,但这是因为runserver有一些技巧来首先验证模型等等。正常部署,甚至当runserver自动重新加载时,这只执行一次。

如果你想在运行服务器时打印一次“hello world”,把print(“hello world”)放到类StartupMiddleware之外

from django.core.exceptions import MiddlewareNotUsed
from django.conf import settings

class StartupMiddleware(object):
    def __init__(self):
        #print "Hello world"
        raise MiddlewareNotUsed('Startup complete')

print "Hello world"

如果它能帮助某些人,除了pykler的答案之外,"——noreload"选项可以防止runserver在启动时执行两次命令:

python manage.py runserver --noreload

但该命令也不会在其他代码更改后重新加载runserver。

注意,您不能可靠地连接到数据库或与AppConfig内的模型交互。Ready函数(参见文档中的警告)。

如果需要在启动代码中与数据库交互,一种可能是使用connection_created信号在连接到数据库时执行初始化代码。

from django.dispatch import receiver
from django.db.backends.signals import connection_created

@receiver(connection_created)
def my_receiver(connection, **kwargs):
    with connection.cursor() as cursor:
        # do something to the database

显然,这个解决方案是为每个数据库连接运行一次代码,而不是在每个项目启动时运行一次。因此,您需要为CONN_MAX_AGE设置一个合理的值,这样就不会对每个请求重新运行初始化代码。还要注意,开发服务器忽略了CONN_MAX_AGE,因此您将在开发过程中对每个请求运行一次代码。

99%的情况下,这是一个坏主意——数据库初始化代码应该放在迁移中——但在某些用例中,您无法避免延迟初始化,上面的警告是可以接受的。