Django가 ONCE 만 시작할 때 코드를 실행 하시겠습니까?


177

시작시 한 번만 실행하여 다른 임의의 코드를 초기화하려는 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목록에 포함되어 있습니다.

그러나 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"가 두 번 인쇄되는 이유가 있습니까? 감사.


1
호기심을 위해서 init .py 의 코드 가 두 번 실행되는 이유를 알았 습니까?
Mutant

3
@Mutant 그것은 runserver에서 두 번만 실행됩니다 ... runserver는 먼저 앱을로드하여 앱을 검사 한 다음 실제로 서버를 시작하기 때문입니다. runserver를 자동으로 다시로드하더라도 코드는 한 번만 실행됩니다.
Pykler

1
와우 나는 여기에 있었다.… 그래서 @Pykler의 의견에 대해 다시 감사합니다, 그것이 내가 궁금했던 것입니다.
WesternGun

답변:


112

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()

1
@Andrei : 관리 명령은 완전히 별개의 문제입니다. 모든 관리 명령 이전의 특별한 일회성 시작 아이디어는 이해하기 어렵습니다. 특정 정보 를 제공해야합니다 . 아마도 다른 질문에 있습니다.
S.Lott

1
urls.py에서 간단한 텍스트 인쇄를 시도했지만 출력이 전혀 없었습니다. 무슨 일이야?
Steve K

8
urls.py 코드는 첫 번째 요청에서만 실행됩니다 (@SteveK의 질문에 답합니다) (django 1.5)
lajarre

4
이것은 각 작업자마다 한 번씩 실행되며, 제 경우에는 총 3 번 실행됩니다.
Raphael

9
@halilpazarlama이 답변은 최신 정보가 아닙니다. Pykler의 답변을 사용해야합니다.
Mark Chackerian

271

업데이트 : 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

첫 번째 답변은 더 이상 작동하지 않는 것 같습니다. urls.py는 첫 번째 요청시로드됩니다.

최근에 작동 한 것은 시작 코드를 INSTALLED_APPS init .py 중 하나에 넣는 것입니다.myapp/__init__.py

def startup():
    pass # load a big thing

startup()

./manage.py runserver...를 사용할 때 이것은 두 번 실행되지만 runserver에는 모델을 먼저 검증하는 트릭이 있습니다 ... 정상 배포 또는 runserver 자동 다시로드 할 때도 한 번만 실행됩니다.


4
나는 이것이 프로젝트를로드하는 각 프로세스마다 실행된다고 생각합니다. 따라서 배포 시나리오에서 이것이 왜 완벽하게 작동하지 않는지 생각할 수 없습니다. 이것은 관리 명령에서 작동합니다. +1
Skylar Saveland

2
서버가 시작될 때이 솔루션을 사용하여 임의의 코드를 실행할 수 있지만 로드 될 데이터 를 공유 할 수 있습니까? 예를 들어, 거대한 행렬을 포함하는 객체를로드 하고이 행렬을 변수에 넣고 웹 API를 통해 사용자가 할 수있는 각 요청마다 사용하려고합니다. 그런 일이 가능합니까?
Patrick

2
문서에 따르면 데이터베이스 상호 작용을 수행 할 수 없습니다. 따라서 많은 코드에 적합하지 않습니다. 이 코드는 어디로 갈 수 있습니까?
Mark

3
편집 : 가능한 해킹 명령 줄 인수 any ([ 'makemigrations', 'migrate']에서 x에 대한 sys.argv의 x)를 확인하는 것입니다.
Conchylicultor

2
스크립트가 두 번 실행하는 경우이 답을 체크 아웃 : stackoverflow.com/a/28504072/5443056
브레이든 홀트

37

이 질문은 Django> = 1.4에서 작동하는 Django 프로젝트 의 블로그 게시물 진입 점 후크 에서 잘 대답됩니다 .

기본적으로이를 사용 <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()

이 메소드가 코드를 한 번만 실행하는지 확인하기 위해 주석을 다시 추가하십시오. 잠금 장치가 필요 없습니다.
ATOzTOA

테스트 프레임 워크가 시작될 때 여기에 추가 된 스크립트가 실행되지 않는 것 같습니다
Lewisou

이 답변은 단순히 작동하지 않는 솔루션에 대한 2, ​​반일의 검색을 종료했습니다.
Neil Munro

3
이것은 Apache를 시작할 때가 아니라 웹 사이트에 대한 첫 번째 요청이있을 때 실행됩니다.
user984003

18

pykler의 답변 외에도 "--noreload"옵션을 사용하면 시작시 runserver가 명령을 두 번 실행할 수 없습니다.

python manage.py runserver --noreload

그러나이 명령은 다른 코드 변경 후에도 실행 서버를 다시로드하지 않습니다.


1
고마워 내 문제를 해결했습니다! 배포 할 때 이런 일이 일어나지 않기를 바랍니다.
Gabo

2
대안으로, os.environ.get('RUN_MAIN')메인 프로세스에서 코드를 한 번만 실행 하는 내용을 확인할 수 있습니다 ( stackoverflow.com/a/28504072 참조 )
bdoering

예,이 플러스 피클 러 (Pykler)의 답변은 여러 번의 ready(self)전화를 막 으면서도 한 번만 시작할 수 있기 때문에 나에게도 효과적이었습니다 . 건배!
DarkCygnus

Django runserver는 기본적으로 고유 한 (다른) pid 숫자로 두 개의 프로세스를 시작합니다. --noreload하나의 프로세스를 시작합니다.
유진 Gr. 빌립보

15

@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

12
프로덕션 모드에서 실행됩니까? 제품의 AFAIK "실행 서버"가 시작되지 않았습니다.
nerdoc

감사합니다! 내가 가진 고급 파이썬 스케줄러를 내 응용 프로그램에서 나는 manage.py 명령을 실행할 때 스케줄러를 실행하지 않았다.
lukik

4

당신이주의 할 수없는 내부 모델 데이터베이스 또는 상호 작용에 연결 신뢰성 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 %의 시간은 나쁜 생각입니다. 데이터베이스 초기화 코드는 마이그레이션에 들어가야합니다. 그러나 늦은 초기화를 피할 수없고 위의 경고를 수용 할 수있는 사용 사례가 있습니다.


2
시작 코드에서 데이터베이스에 액세스해야하는 경우 좋은 솔루션입니다. 한 번만 실행되도록하는 간단한 방법은 my_receiver함수가 connection_created신호 에서 분리 되도록하는 my_receiverconnection_created.disconnect(my_receiver)입니다. 특히 함수에 다음을 추가하십시오 .
alan

1

서버를 실행할 때 "hello world"를 한 번 인쇄하려면 StartupMiddleware 클래스에서 print ( "hello world")를 넣습니다.

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"

3
안녕 오스카! 따라서 답변에는 코드뿐만 아니라 영어로 된 설명이 포함되는 것이 좋습니다. 코드가 어떻게 /이 문제를 해결하는지에 대한 간단한 설명을 제공해 주시겠습니까?
Max von Hippel
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.