Выполнить код, когда Django запускается только ОДИН РАЗ? - PullRequest
143 голосов
/ 22 июля 2011

Я пишу класс промежуточного программного обеспечения 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.

Но когда я запускаю Django с помощью runserver и запрашиваю страницу, яполучить в терминале

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» печатается дважды?Спасибо.

Ответы [ 7 ]

222 голосов
/ 19 апреля 2013

Обновление: Django 1.7 теперь имеет хук для этого

файл: myapp/apps.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'

Для Джанго <1,7 </h2> Ответ номер один больше не работает, urls.py загружается при первом запросе. В последнее время работает то, что вы помещаете код запуска в любой из ваших INSTALLED_APPS init .py, например. myapp/__init__.py def startup(): pass # load a big thing startup() При использовании ./manage.py runserver ... это выполняется дважды, но это потому, что у runserver есть некоторые хитрости для проверки моделей в первую очередь и т. Д. ... при нормальном развертывании или даже при автоматической перезагрузке runserver это выполняется только один раз.

87 голосов
/ 22 июля 2011

Обновление из ответа Пиклера ниже: Django 1.7 теперь имеет хук для этого


Не делай так.

Вам не нужно «промежуточное ПО» для единовременного запуска.

Вы хотите выполнить код на верхнем уровне urls.py. Этот модуль импортируется и выполняется один раз.

urls.py

from django.confs.urls.defaults import *
from my_app import one_time_startup

urlpatterns = ...

one_time_startup()
34 голосов
/ 30 октября 2013

На этот вопрос хорошо ответили в сообщении в блоге Точка входа для проектов 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()
15 голосов
/ 10 сентября 2015

Если это кому-то поможет, в дополнение к ответу pykler , опция --noreload не позволяет runserver выполнять команду при запуске дважды:

python manage.py runserver --noreload

Но эта команда не будетперезагрузите runserver после других изменений кода.

9 голосов
/ 28 февраля 2018

Как подсказывает @Pykler, в Django 1.7+ вы должны использовать ловушку, объясненную в его ответе, но если вы хотите, чтобы , ваша функция вызывалась только тогда, когда сервер запуска вызывается (а не при выполнении миграций , миграция, оболочка и т. д.), и вы хотите избежать исключений AppRegistryNotReady вы должны сделать следующее:

файл: myapp/apps.py

import sys
from django.apps import AppConfig

class MyAppConfig(AppConfig):
    name = 'myapp'
    verbose_name = 'My Application'

    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
4 голосов
/ 11 октября 2017

Обратите внимание, что вы не можете надежно подключиться к базе данных или взаимодействовать с моделями внутри функции 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% случаев это плохая идея - код инициализации базы данных должен идти в процессе миграции - но есть некоторые случаи использования, когда вы не можете избежать поздней инициализации, и приведенные выше предостережения приемлемы.

0 голосов
/ 02 марта 2018

если вы хотите печатать «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"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...