revision-up-to: | 8961 (1.0) |
---|
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
モデルの delete() メソッド呼び出しの前 後に送信されます。
django.core.signals.request_started および django.core.signals.request_finished
Django が HTTP リクエストを処理する直前直後に送信されます。
組み込みシグナルの全容と各々の解説は、 組み込みシグナルのドキュメント で解説しています。
シグナルは 自分て定義したり送信したり できます。詳しくは以下を参照し てください。
シグナルを受信するには、シグナルが送信されたときに呼び出される レシーバ(receiver) 関数を登録する必要があります。 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 はエラーを送出します。というのも、将来シグナルに引数が追加されるかも しれず、そのときにもレシーバは新たに追加された引数を扱えねばならないからで す。
次に、レシーバをシグナルに結びつけます:
from django.core.signals import request_finished
request_finished.connect(my_callback)
これで、 my_callback 関数は、リクエストの処理が終了するたびに呼び出され ます。
シグナル処理のコードはどこにおけばよいのですか?
シグナルの処理や登録のためのコードは、どこでも好きな場所に置けます。 とはいえ、自分の登録したいシグナルが送信されるよりも前に、コードの入っ ているモジュールを早々に import しておきたいいでしょう。そのため、シグ ナルハンドラの登録は models.py に置くのがよいでしょう。
シグナルには何度も送信されるものがありますが、その中でも特定のサブセットだ けを受け取りたい場合もあるでしょう。例えば、モデルインスタンスが保存される ときに送信される django.db.models.signals.pre_save を考えましょう。 大抵は、シグナルを受信したいのは すべての モデルの保存時ではなく、 特定の モデルの保存時のはずです。
こうしたケースのために、特定のセンダから送られるシグナルに対してレシーバを 登録できます。 django.db.models.signals.pre_save の場合、センダは インスタンスを保存しようとするモデルなので、以下のようにして、特定のモデル に対してのみシグナルを受信させられます:
from django.db.models.signals import pre_save
from myapp.models import MyModel
def my_handler(sender, **kwargs):
...
pre_save.connect(my_handler, sender=MyModel)
センダに入るオブジェクトは、シグナルによって異なります。個々のシグナルにつ いての情報は 組み込みシグナルのドキュメント を参照して ください。
自分のアプリケーション内でも、シグナルのインフラストラクチャを使ったり、独 自のシグナルを提供したりできます。
シグナルは全て django.dispatch.Signal のインスタンスです。 providing_args は、シグナルがリスナに対して提供する引数の名前が入ったリ ストです。
例えば:
import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
上のコードは pizza_done シグナルを作成し、このシグナルが topping と size という引数をもたらすことを宣言しています。
このリストはいつでも変更できるので、最初の時点で正確に API を定義しておく必 要はありません。
シグナルを送信するには Signal.send() を呼び出します。 sender を必 ず指定し、必要に応じて追加の引数を指定します。
例えば、 pizza_done シグナルを送信するには、以下のように書きます:
class PizzaStore(object):
...
def send_pizza(self, toppings, size):
pizza_done.send(sender=self, toppings=toppings, size=size)
...
Aug 31, 2012