revision-up-to: | 17812 (1.4) |
---|
Django には「シグナルディスパッチャ (signal dispatcher)」が組み込まれていま す。シグナルディスパッチャにより、アプリケーションをフレームワークから脱カッ プリングしつつ、フレームワークのどこかで起きたアクションに応じた通知を受け られます。簡単にいえば、シグナルによって、ある「 送信側(sender) 」から 複数の「 受信側(receiver) 」に向けて、何らかのアクションが起きたことを通 知できるのです。シグナルは、たくさんのコードが同じイベントを待ち受けるよう な状況で特に便利です。
Django は、自分自身の特定のアクションを通知するための 組み込みシグナ ル を提供しています。組み込みシグナルの中には、以下のような 便利なものがあります:
django.db.models.signals.pre_save
および
django.db.models.signals.post_save
モデルの save()
メソッド呼び出しの前後
で送信されます。
django.db.models.signals.pre_delete
および
django.db.models.signals.post_delete
django.db.models.signals.m2m_changed
ManyToManyField
が変更されたときに送信されます。
django.core.signals.request_started
および
django.core.signals.request_finished
Django が HTTP リクエストを処理する直前直後に送信されます。
組み込みシグナルの全容と各々の解説は、 組み込みシグナルのドキュメント で解説しています。
シグナルは 自分で定義したり送信したり できます。詳しくは以下を参照し てください。
シグナルを受信するには、シグナルが送信されたときに呼び出される レシーバ
(receiver) 関数を、 Signal.connect()
メソッドを使って登録する必要が
あります。
Signal.
connect
(receiver[, sender=None, weak=True, dispatch_uid=None])¶Parameters: |
|
---|
HTTP リクエストの処理が終ったときに呼び出されるシグナルのレシーバを登録して、
この仕組みを見てみましょう。この例では、
request_finished
シグナルをレシーバに結びつけます。
まず、レシーバ関数を定義します。レシーバは通常の Python の関数やメソッドと して定義します:
def my_callback(sender, **kwargs):
print "Request finished!"
この関数は固定引数の sender
と、ワイルドカードで表現された任意のキーワー
ド引数 (**kwargs
) をとります。シグナルハンドラは、全てこの形式の引数を
とらねばなりません。
sender
については 後で 説明するとして、今はまず
**kwargs
に注目しましょう。シグナルはすべてキーワード引数を伴い、キーワー
ド引数の内容はいつでも変更される可能性があります。
request_finished
の場合、ドキュメントにキーワー
ド引数をもたないと明記されているので、シグナルハンドラを
my_callback(sender)
と書いていいと思いがちです。
しかしこれは誤っています。実際、 my_callback(sender)
のように定義すると
Django はエラーを送出します。というのも、将来シグナルに引数が追加されるかも
しれず、そのときにもレシーバは新たに追加された引数を扱えねばならないからで
す。
レシーバをシグナルに結びつけるには 2 つの方法があります。手動での接続方法は 以下のようになります:
from django.core.signals import request_finished
request_finished.connect(my_callback)
あるいは、 receiver
デコレータを使う方法があります。レシーバを定義するときに
使います:
from django.core.signals import request_finished
from django.dispatch import receiver
@receiver(request_finished)
def my_callback(sender, **kwargs):
print "Request finished!"
これで、 my_callback
関数は、リクエストの処理が終了するたびに呼び出され
ます。
リリースノートを参照してください
receiver
デコレータは Django 1.3 で追加されました。
シグナル処理のコードはどこにおけばよいのですか?
シグナルの処理や登録のためのコードは、どこでも好きな場所に置けます。
とはいえ、自分の登録したいシグナルが送信されるよりも前に、コードの入っ
ているモジュールを早々に import しておきたいいでしょう。そのため、シグ
ナルハンドラの登録は models.py
に置くのがよいでしょう。
シグナルには何度も送信されるものがありますが、その中でも特定のサブセットだ
けを受け取りたい場合もあるでしょう。例えば、モデルインスタンスが保存される
ときに送信される django.db.models.signals.pre_save
を考えましょう。
大抵は、シグナルを受信したいのは すべての モデルの保存時ではなく、
特定の モデルの保存時のはずです。
こうしたケースのために、特定のセンダから送られるシグナルに対してレシーバを
登録できます。 django.db.models.signals.pre_save
の場合、センダは
インスタンスを保存しようとするモデルなので、以下のようにして、特定のモデル
に対してのみシグナルを受信させられます:
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):
...
これで、保存されるインスタンスが MyModel
のインスタンスであるときだけ、
my_handler
関数が呼び出されます。
センダに入るオブジェクトは、シグナルによって異なります。個々のシグナルにつ いての情報は 組み込みシグナルのドキュメント を参照して ください。
いくつかの状況では、シグナルに結びつけたモジュールが複数回インポートされます。 これはレシーバ関数が 2 回以上登録されている可能性があり、 1 つのシグナルイベント が複数回呼び出されています。
この振る舞いに問題があるとき (例えばモデルが保存されたときにメールを送るシグナル
を使うなど) は、ユニークな識別子である dispatch_uid
引数をレシーバ関数に
渡してください。普通この識別子は文字列ですが、ハッシュ可能オブジェクトも使え
ます。個々のユニークな dispatch_uid
値のため、レシーバ関数はシグナルに 1 度
だけ結び付けられます。
from django.core.signals import request_finished
request_finished.connect(my_callback, dispatch_uid="my_unique_identifier")
自分のアプリケーション内でも、シグナルのインフラストラクチャを使ったり、独 自のシグナルを提供したりできます。
Signal
([providing_args=list])¶シグナルは全て django.dispatch.Signal
のインスタンスです。
providing_args
は、シグナルがリスナに対して提供する引数の名前が入ったリ
ストです。
例えば:
import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
上のコードは pizza_done
シグナルを作成し、このシグナルが topping
と
size
という引数をもたらすことを宣言しています。
このリストはいつでも変更できるので、最初の時点で正確に API を定義しておく必 要はありません。
Django にはシグナルを送信する方法が 2 つあります。
シグナルを送信するには Signal.send()
か Signal.send_robust()
を
呼び出します。 sender
を必ず指定し、必要に応じて追加の引数を指定します。
例えば、 pizza_done
シグナルを送信するには、以下のように書きます:
class PizzaStore(object):
...
def send_pizza(self, toppings, size):
pizza_done.send(sender=self, toppings=toppings, size=size)
...
send()
と send_robust()
の両方とも、要素数 2 のタプルのリスト
[(receiver, response), ... ]
を返します。呼び出されたレシーバ関数とその
レスポンス値が、リストになっています。
send()
と send_robust()
の違いは、レシーバ関数から送出された例外をどの
ように処理するかです。 send()
はレシーバから送出される例外を一切 捕らえま
せん 。単純にエラーが伝播されます。レシーバがエラーを起こすと、シグナルは通知
されません。
send_robust()
は Python の Exception
クラスから送出されるエラーを捕ら
えます。シグナルがレシーバに通知されることを保証します。レシーバがエラーを送出
するとエラーインスタンス (そのレシーバとエラー内容のタプル) が返されます。
Signal.
disconnect
([receiver=None, sender=None, weak=True, dispatch_uid=None])¶シグナルとレシーバの接続を断つには Signal.disconnect()
を呼び出します。
引数は Signal.connect()
に述べられている通りです。
receiver 引数には、接続を断ちたい登録済みのレシーバを指定します。
レシーバを識別するために dispath_uid
使われている場合、 receiver は
None
で構いません。
Oct 26, 2017