Я пишу класс Django Middleware, который я хочу выполнить только один раз при запуске, чтобы инициализировать некоторый другой условный код. Я следил за очень хорошим решением, опубликованным 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
.
Но когда я запускаю 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" печатается дважды? Спасибо.
Обновление от Pykler ответ ниже: Django 1.7 теперь имеет hook для этого
Не делайте этого так.
Вам не требуется "промежуточное ПО" для однократной загрузки.
Вы хотите выполнить код на верхнем уровне urls.py
. Этот модуль импортируется и выполняется один раз.
urls.py
from django.confs.urls.defaults import *
from my_app import one_time_startup
urlpatterns = ...
one_time_startup()
file: myapp/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'myapp'
verbose_name = "My Application"
def ready(self):
pass # startup code here
file: myapp/__init__.py
default_app_config = 'myapp.apps.MyAppConfig'
Ответ на номер один больше не работает, urls.py загружается по первому запросу.
В последнее время работает то, что поставить код запуска в любой из ваших INSTALLED_APPS init.py, например. myapp/__init__.py
def startup():
pass # load a big thing
startup()
При использовании ./manage.py runserver
... это выполняется два раза, но это связано с тем, что у запускающего сервера есть некоторые трюки, чтобы проверить модели сначала и т.д.... нормальные развертывания или даже при перезагрузке автозагрузки при загрузке, это выполняется только один раз.
Этот вопрос хорошо ответил в сообщении блога Крюк точки входа для проектов 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()
Если это помогает кому-то, помимо ответа pykler, опция "-noreload" не позволяет запускающему сценарию запуска запуска при запуске дважды:
python manage.py runserver --noreload
Но эта команда не перезагружает серверы после других изменений кода.
os.environ.get('RUN_MAIN')
чтобы выполнить код только один раз в основном процессе (см. Stackoverflow.com/a/28504072 )
Обратите внимание: вы не можете надежно подключиться к базе данных или взаимодействовать с моделями внутри функции 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% случаев это плохая идея - код инициализации базы данных должен идти в миграции - но есть некоторые варианты использования, когда вы не можете избежать поздней инициализации, и вышеописанные предостережения приемлемы.
my_receiver
функцию my_receiver
отключиться от сигнала connection_created
, в частности добавить следующее в функцию my_receiver
: connection_created.disconnect(my_receiver)
.