我正在编写一个Django Middleware类,我想在启动时只执行一次,以初始化其他一些arbritary代码.我已经按照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')
Run Code Online (Sandbox Code Playgroud)
在我的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
Run Code Online (Sandbox Code Playgroud)
任何有关"Hello world"打印两次的想法?谢谢.
Pyk*_*ler 239
文件: myapp/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'myapp'
verbose_name = "My Application"
def ready(self):
pass # startup code here
Run Code Online (Sandbox Code Playgroud)
文件: myapp/__init__.py
default_app_config = 'myapp.apps.MyAppConfig'
Run Code Online (Sandbox Code Playgroud)
第一个答案似乎不再起作用,urls.py在第一次请求时加载.
最近工作的是将启动代码放在任何一个INSTALLED_APPS init .py中myapp/__init__.py
def startup():
pass # load a big thing
startup()
Run Code Online (Sandbox Code Playgroud)
使用时./manage.py runserver...这会被执行两次,但这是因为runserver有一些技巧可以首先验证模型等...正常部署甚至当runserver自动重新加载时,这只执行一次.
S.L*_*ott 98
更新Pykler的答案如下:Django 1.7现在有一个钩子
不要这样做.
你不希望"中间件"用于一次性启动.
您想在顶级执行代码urls.py.该模块导入并执行一次.
urls.py
from django.confs.urls.defaults import *
from my_app import one_time_startup
urlpatterns = ...
one_time_startup()
Run Code Online (Sandbox Code Playgroud)
aug*_*men 36
这个问题在Django项目的入口点钩子的博客文章中得到了很好的回答,它适用于Django> = 1.4.
基本上,您可以使用它<project>/wsgi.py来执行此操作,并且它将仅在服务器启动时运行一次,而不是在运行命令或导入特定模块时运行.
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ project_name }}.settings")
# Run startup code!
....
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
Run Code Online (Sandbox Code Playgroud)
J_Z*_*Zar 17
使用 Django 3.1+,您可以编写此代码以在启动时仅执行一次方法。与其他问题的不同之处在于,检查了主启动进程(runserver 默认启动 2 个进程,其中一个作为快速代码重新加载的观察者):
import os
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'app_name'
def ready(self):
if os.environ.get('RUN_MAIN'):
print("STARTUP AND EXECUTE HERE ONCE.")
# call here your code
Run Code Online (Sandbox Code Playgroud)
另一种解决方案是避免环境检查,但调用 --noreload 来强制仅一个进程。
首先要回答的问题是为什么我们需要执行一次代码:通常我们需要初始化一些服务、数据库中的数据或一次性的东西。90% 的时间是一些数据库初始化或作业队列。
使用 AppConfig.ready() 方法的方法并不可靠,在生产中并不总是可重现,并且不能保证恰好执行一次(但至少执行一次不一样)。要让某些事情完全可预测并执行一次,最好的方法是开发一个 Django BaseCommand 并从启动脚本中调用它。
例如,我们可以在文件“app/management/commands/init_tasks.py”中的“myapp”中进行编码:
from django.core.management.base import BaseCommand
from project.apps.myapp.tasks import scheduler
from project import logger, initialize_database_data
class Command(BaseCommand):
help = "Init scheduler or do some staff in the database."
def handle(self, *args, **options):
scheduler.reload_jobs()
initialize_database_data()
logger.info("Inited")
Run Code Online (Sandbox Code Playgroud)
最后,我们可以使用启动脚本“Start.bat”(在示例中是 Windows 批处理)来设置完整的应用程序启动:
start /b python manage.py qcluster
start /b python manage.py runserver 0.0.0.0:8000
start /b python manage.py init_tasks
Run Code Online (Sandbox Code Playgroud)
Ana*_*ana 16
如果它对某人有帮助,除了pykler的回答之外,"--noreload"选项会阻止runserver在启动时执行两次命令:
python manage.py runserver --noreload
Run Code Online (Sandbox Code Playgroud)
但是,在其他代码更改之后,该命令也不会重新加载runserver.
小智 12
正如@Pykler建议,在Django 1.7+,你应该使用挂钩,在他的回答解释,但如果你想要一个你的函数只调用时运行服务器称为(而不是制作时迁移,迁移,外壳等被称为),你想要避免AppRegistryNotReady例外,你必须做如下:
文件: myapp/apps.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
Run Code Online (Sandbox Code Playgroud)
请注意,您无法可靠地连接到数据库或与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
Run Code Online (Sandbox Code Playgroud)
显然,这个解决方案是为每个数据库连接运行一次代码,而不是每个项目启动一次。所以你需要一个合理的CONN_MAX_AGE设置值,这样你就不会在每个请求上重新运行初始化代码。另请注意,开发服务器会忽略CONN_MAX_AGE,因此您将在开发中为每个请求运行一次代码。
99% 的情况下这是一个坏主意 - 数据库初始化代码应该在迁移中进行 - 但在某些用例中,您无法避免延迟初始化,并且上述警告是可以接受的。
| 归档时间: |
|
| 查看次数: |
69319 次 |
| 最近记录: |