DjangoがONCEのみを起動したときにコードを実行しますか?


176

起動時に1回だけ実行して、他の任意のコードを初期化したいDjango Middlewareクラスを書いています。ここでは、sdolanが投稿した非常に優れた解決策に従っていますが、「Hello」メッセージが端末に2回出力されます。例えば

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」が2度印刷される理由はありますか?ありがとう。


1
好奇心のために、init .py のコードが2回実行される理由を理解しましたか?
ミュータント

3
@Mutantそれはrunserverの下で2回だけ実行されます...これは、runserverが最初にアプリをロードして検査し、実際にサーバーを起動するためです。runserverのオートリロード時でも、コードは一度だけ実行されます。
ピクラー2013

1
わあ、ここに来ました。コメント@Pyklerにもう一度感謝します。
WesternGun 2018年

回答:


111

以下の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:管理コマンドは完全に別の問題です。すべての管理コマンドの前に特別な1回限りの起動を行うという考えは、理解するのが困難です。具体的なものを提供する必要があります。おそらく別の質問です。
S.Lott

1
urls.pyに単純なテキストを印刷しようとしましたが、まったく出力がありませんでした。何が起こっている ?
スティーブK

8
urls.pyコードは最初のリクエストでのみ実行されます(@SteveKの質問に答えると思います)(django 1.5)
lajarre

4
これは、ワーカーごとに1回実行されます。私の場合、合計3回実行されます。
ラファエル2014年

9
@halilpazarlamaこの回答は古くなっています-Pyklerからの回答を使用する必要があります。
Mark Chackerian、2015年

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'

Django <1.7の場合

一番の答えはもう機能していないようです。最初のリクエストでurls.pyがロードされます。

最近機能しているのは、起動コードをINSTALLED_APPS init .pyのいずれかに配置することです。myapp/__init__.py

def startup():
    pass # load a big thing

startup()

使用すると./manage.py runserver...これは2回実行されますが、それは、runserverが最初にモデルを検証するなどのトリックがあるためです。


4
これは、プロジェクトをロードするプロセスごとに実行されると思います。したがって、どの展開シナリオでもこれが完全に機能しない理由を考えることはできません。これは管理コマンドでは機能します。+1
スカイラーセーブランド2013

2
このソリューションを使用して、サーバーの起動時に任意のコードを実行できることを理解していますが、ロードされるデータを共有することは可能ですか?たとえば、巨大なマトリックスを含むオブジェクトをロードし、このマトリックスを変数に入れて、ユーザーが実行できる各リクエストでWeb APIを介して使用したいとします。そんなことありますか?
Patrick

2
ドキュメントは、これはデータベースの相互作用を行う場所ではないと述べています。そのため、多くのコードには適していません。このコードはどこに行くことができますか?
マーク

3
編集:可能なハックは、コマンドライン引数any(x in sys.argv for x in ['makemigrations'、 'migrate'])をチェックすることです
Conchylicultor

2
スクリプトが2回実行されている場合は、この答えを確認してください。stackoverflow.com
Braden Holt

37

この質問は、Django> = 1.4で機能するDjangoプロジェクトのブログエントリポイントフックでよく回答されています。

基本的に、これを行うために使用でき<project>/wsgi.py、サーバーの起動時に1回だけ実行されますが、コマンドの実行時や特定のモジュールのインポート時には実行されません。

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

もう一度コメントを追加して、このメソッドがコードを1回だけ実行することを確認します。ロック機構は必要ありません。
ATOzTOA 2014年

ここに追加されたスクリプトは、テストフレームワークの開始時に実行されないようです
Lewisou

この答えにより、単に機能しないソリューションの2日半の検索が終了しました。
Neil Munro

3
これは、Apacheを起動したときではなく、最初のリクエストがWebサイトに対して行われたときに実行されることに注意してください。
user984003

18

それが誰かを助けるなら、pyklerの答えに加えて、「-noreload」オプションはrunserverが起動時にコマンドを2回実行することを防ぎます:

python manage.py runserver --noreload

しかし、そのコマンドは他のコードの変更後もrunserverをリロードしません。


1
これで私の問題は解決しました!展開してもこれが起こらないことを願っています
Gabo

2
別の方法として、os.environ.get('RUN_MAIN')メインプロセスでコードを1回だけ実行するように内容を確認できます(stackoverflow.com/a/28504072を参照)
bdoering

うん、これに加えてpyklerの答えもうまくいきました。それは複数回のready(self)呼び出しを防ぎながら、まだ一度しか起動できないからです。乾杯!
DarkCygnus

Django runserverはデフォルトで、異なる(異なる)pid番号で2つのプロセスを開始します。--noreload1つのプロセスを開始させます。
Eugene 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

これをありがとう!私が持っている高度なPythonのスケジューラを私のアプリでは、私はmanage.pyのコマンドを実行しているときに、スケジューラを実行したくありませんでした。
lukik

4

信頼性の高い方法でデータベースに接続したり、関数内のモデルを操作したりすることはできませんAppConfig.ready警告を参照)ドキュメントのを)。

起動コードでデータベースを操作する必要がある場合、1つの可能性は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

明らかに、このソリューションは、データベース接続ごとに1回コードを実行するためのものであり、プロジェクトの開始ごとに1回実行するためのものではありません。そのため、CONN_MAX_AGEすべてのリクエストで初期化コードを再実行しないように、設定には適切な値が必要です。また、開発サーバーは無視することに注意してくださいCONN_MAX_AGEため、開発のリクエストごとに1回コードを実行します。

99%の確率でこれは悪い考えです。データベースの初期化コードは移行に使用する必要がありますが、初期化の遅れを回避できないユースケースがあり、上記の警告は許容されます。


2
これは、起動コードでデータベースにアクセスする必要がある場合に適したソリューションです。これを1回だけ実行する簡単な方法は、my_receiver関数からconnection_created信号自体を切断するmy_receiverことですconnection_created.disconnect(my_receiver)。具体的には、次の関数を追加します。
アラン

1

サーバーを実行するときに一度「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"

3
こんにちはオスカー!SOでは、回答にはコードだけでなく、英語での説明を含めることをお勧めします。あなたのコードがどのように/なぜあなたのコードが問題を修正するのかについて簡単な説明を教えていただけませんか?
Max von Hippel 2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.