web-dev-qa-db-ja.com

Djangoプロジェクトのシグナルハンドラはどこに住むべきですか?

Djangoプロジェクトにシグナルリスナーを実装し始めたところです。それらが何であり、どのように使用するかは理解していますが、どこに置くべきかを考えるのに苦労しています。 Djangoサイトにはこれがあります:

このコードはどこにあるべきですか?

信号処理および登録コードは好きな場所に置くことができます。ただし、信号を送信する必要がある前に信号処理が登録されるように、モジュールが早い段階でインポートされていることを確認する必要があります。これにより、アプリのmodels.pyはシグナルハンドラーの登録を行うのに適した場所になります。

良い提案ではありますが、models.pyに非モデルクラスまたはメソッドがあると、間違った方法でこすられます。

それでは、シグナルハンドラを保存および登録するためのベストプラクティス/ルールは何ですか?

131
Jason Webb

私は実際にそれらをモデル自体のクラスメソッドにするのが好きです。これにより、すべてが1つのクラス内に保持されるため、何もインポートすることを心配する必要がなくなります。

39
Daniel Roseman

これは、 documentation に追加されましたDjango 1.7がリリースされたとき:

厳密に言えば、信号処理と登録コードは好きな場所に置くことができますが、コードのインポートの副作用を最小限に抑えるために、アプリケーションのルートモジュールとそのモデルモジュールを避けることをお勧めします。

実際には、シグナルハンドラは通常、関連するアプリケーションのシグナルサブモジュールで定義されます。シグナルレシーバーは、アプリケーション構成クラスのready()メソッドで接続されます。 receiver()デコレータを使用している場合は、ready()内のシグナルサブモジュールをインポートするだけです。

変更されたDjango 1.7:ready()は以前のバージョンのDjangoには存在しなかったため、信号の登録は通常、modelsモジュールで行われました。

ベストプラクティスは、シグナルサブモジュールのhandlers.pyでハンドラーを定義することです。次のようなファイル:

yourapp/signals/handlers.py

from Django.db.models.signals import pre_save
from Django.dispatch import receiver
from myapp.models import MyModel

@receiver(pre_save, sender=MyModel)
def my_handler(sender, **kwargs):
    pass

シグナルハンドラーを登録する最適な場所は、ready()メソッドを使用して、シグナルハンドラーを定義するアプリのAppConfigです。これは次のようになります。

yourapp/apps.py

from Django.apps import AppConfig

class TasksConfig(AppConfig):
    name = 'tasks'
    verbose_name = "Tasks"

    def ready(self):
        import yourproject.yourapp.signals.handlers #noqa

Settings.pyのINSTALLED_APPSで直接指定するか、__init__アプリの。詳細については、 ready()のドキュメントを参照 を参照してください。

注:他のアプリもリッスンする信号を提供している場合は、__init__あなたのシグナルモジュール、例えば次のようなファイル:

yourapp/signals/__ init __。py

import Django.dispatch

task_generate_pre_save = Django.dispatch.Signal(providing_args=["task"])

次に、別のアプリがインポートして登録することで信号を聞くことができます。 from yourapp.signals import task_generate_pre_save。ハンドラーからシグナルを分離することで、物事がきれいに保たれます。

Django 1.6:の手順

それでもまだDjango 1.6以下)に留まっている場合は、同じことを行います(yourapp/signals/handlers.pyでハンドラーを定義します)が、AppConfigを使用するのではなく、アプリの__init__.pyを介してハンドラーをロードします。例:

yourapp/__ init __。py

import signals

循環インポートの問題が発生することが多いため、これはready()メソッドを使用するほどナイスではありません。

225
Aidan

私はこれに出くわしただけで、私の信号はモデルに関連していないので、ソリューションを追加すると思いました。

ログイン/ログアウトに関するさまざまなデータを記録していますが、Django.contrib.auth.signalsにフックする必要があります。

シグナルハンドラーをsignals.pyファイルに配置し、__init__.pyモジュールファイルからシグナルをインポートしました。これは、アプリが起動するとすぐに呼び出されると思われるためです(printステートメントは、設定ファイルが読み取られる前であっても呼び出されることを示唆しています。

# /project/__init__.py
import signals

そしてsignals.pyで

# /project/signals.py
from Django.contrib.auth.signals import user_logged_in

def on_logged_in(sender, user, request, **kwargs):
    print 'User logged in as: \'{0}\''.format(user)

user_logged_in.connect(on_logged_in)

私はDjango(/ python)が初めてなので、これはひどいアイデアだと言っている人なら誰でも受け入れます!

39

私は最近 this プロジェクト/アプリケーションのレイアウトに関するベストプラクティスに関する記事を読みましたが、カスタムディスパッチャシグナルはすべてsignals.pyというファイルに保存することをお勧めします。ただし、これらをどこかにインポートする必要があるため、問題を完全に解決することはできません。以前にインポートする方が良いためです。

モデルの提案は良いものです。 signals.pyファイルですべてをすでに定義しているので、ファイルの先頭に1行以上かかることはありません。これは、admin.pyファイルのレイアウトに似ています(上部にクラス定義があり、下部にすべてのカスタム管理クラスを登録するためのコードがあります)。信号を定義してから同じファイルに接続する場合。

お役に立てば幸いです!最終的には、あなたが好むものになります。

13
hora

各アプリのmodels.pyとsignals.pyは、シグナルを接続するための推奨される場所ですが、私の意見では、シグナルとハンドラーをディスパッチし続けるのに最適なソリューションではありません。ディスパッチは、Djangoで発明されたシグナルとハンドラーの理由です。

私は長い間苦労していましたが、最終的に解決策を見つけました。

アプリフォルダーにコネクタモジュールを作成

だから私たちは持っています:

app/
    __init__.py
    signals.py
    models.py
    connectors.py

app/connectors.pyで、シグナルハンドラを定義して接続します。以下に例を示します。

from signals import example_signal
from models import ExampleModel
from Django.db.models.signals import post_save, post_delete

def hanndler(sender, *args, **kwargs):
    pass

post_save.connect(hander, sender=ExampleModel)

次に、models.pyで、ファイルの最後に次の行を追加します。

from app import connector

ここで行われたすべて。

このようにして、signals.pyにシグナルを配置し、connectors.pyにすべてのハンドラーを配置できます。モデルと信号に混乱はありません。

別のソリューションが提供されることを願っています。

7
samuel

AppConfigに関する小さなリマインダー。設定することを忘れないでください:

# yourapp/__init__.py

default_app_config = 'yourapp.apps.RockNRollConfig'
3
valex

別のファイルに保管しますsignals.py、_models.pyすべてのモデルが定義された後。それらをインポートし、モデルを信号に接続します。

signals.py

#  necessary imports

def send_mail_on_save(<args>):
    # code here 

models.py

# imports
class mymodel(models.Model):
    # model here

# import signals
from signals import send_mail_on_save
# connect them 
post_save.connect(send_mail_on_save,sender=mymodel)

これにより論理的な分離が得られます。もちろん、models.pyに保持することには何の問題もありませんが、この方法の方が管理しやすいです。

お役に立てれば!!

2
allsyed