我正在编写一个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"打印了两次吗?谢谢。
我使用了这里公认的解决方案,即检查它是否作为服务器运行,而不是在执行其他manage .py命令(如migrate)时检查
apps.py:
from .tasks import tasks
class myAppConfig(AppConfig):
...
def ready(self, *args, **kwargs):
is_manage_py = any(arg.casefold().endswith("manage.py") for arg in sys.argv)
is_runserver = any(arg.casefold() == "runserver" for arg in sys.argv)
if (is_manage_py and is_runserver) or (not is_manage_py):
tasks.is_running_as_server = True
由于这仍然会在开发模式下执行两次,不使用参数——noreload,我添加了一个标志,当它作为服务器运行时触发,并将我的启动代码放在urls.py中,只调用一次。
tasks.py:
class tasks():
is_running_as_server = False
def runtask(msg):
print(msg)
urls . py:
from . import tasks
task1 = tasks.tasks()
if task1.is_running_as_server:
task1.runtask('This should print once and only when running as a server')
总之,我利用AppConfig中的read()函数来读取参数并了解代码是如何执行的。但是由于在开发模式下,ready()函数运行了两次,一次用于服务器,一次用于在代码更改时重新加载服务器,而urls.py仅为服务器执行了一次。所以在我的解决方案中,我将两者结合起来,只在代码作为服务器执行时运行我的任务。
注意,您不能可靠地连接到数据库或与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%的情况下,这是一个坏主意——数据库初始化代码应该放在迁移中——但在某些用例中,您无法避免延迟初始化,上面的警告是可以接受的。
我使用了这里公认的解决方案,即检查它是否作为服务器运行,而不是在执行其他manage .py命令(如migrate)时检查
apps.py:
from .tasks import tasks
class myAppConfig(AppConfig):
...
def ready(self, *args, **kwargs):
is_manage_py = any(arg.casefold().endswith("manage.py") for arg in sys.argv)
is_runserver = any(arg.casefold() == "runserver" for arg in sys.argv)
if (is_manage_py and is_runserver) or (not is_manage_py):
tasks.is_running_as_server = True
由于这仍然会在开发模式下执行两次,不使用参数——noreload,我添加了一个标志,当它作为服务器运行时触发,并将我的启动代码放在urls.py中,只调用一次。
tasks.py:
class tasks():
is_running_as_server = False
def runtask(msg):
print(msg)
urls . py:
from . import tasks
task1 = tasks.tasks()
if task1.is_running_as_server:
task1.runtask('This should print once and only when running as a server')
总之,我利用AppConfig中的read()函数来读取参数并了解代码是如何执行的。但是由于在开发模式下,ready()函数运行了两次,一次用于服务器,一次用于在代码更改时重新加载服务器,而urls.py仅为服务器执行了一次。所以在我的解决方案中,我将两者结合起来,只在代码作为服务器执行时运行我的任务。
在我的例子中,我使用Django来托管一个站点,并使用Heroku。我在Heroku使用1个dyno(就像1个容器),这个dyno创建了两个工人。
我想在上面运行一个不和机器人。我尝试了这个页面上的所有方法,都是无效的。
因为它是一个部署,所以不应该使用manage.py。相反,它使用gunicorn,我不知道如何添加-noreload参数。
每个worker运行一次wsgi.py,因此每个代码将运行两次。两个工人的当地环境是一样的。
但我注意到一件事,每次Heroku部署,它使用相同的pid工作。所以我只是
if not sys.argv[1] in ["makemigrations", "migrate"]: # Prevent execute in some manage command
if os.getpid() == 1: # You should check which pid Heroku will use and choose one.
code_I_want_excute_once_only()
我不确定pid在未来是否会改变,希望它永远是一样的。如果你有更好的方法来检查是哪个工人,请告诉我。