読者です 読者をやめる 読者になる 読者になる

django.dispatchを使ってイベントを捕捉する

Django Python

 Djangoにはdjango.dispatchというモジュールがあり、リクエスト処理開始/終了、モデル・オブジェクトの保存/消去といったイベントを捕捉して、イベント発生時に任意の関数を呼び出すことができます。


 CocoaだとNSNotificationCenter & NSNotification、QTだとSIGNAL & SLOT, Boostだとboost::signal、といった具合に、使われる用語は様々ですが、どんなライブラリ・フレームワークにも必ずある、例のアレです。
 django.dispatchモジュールについては、本家のドキュメントにもTipsにも言及がないのですが、*1Django内部では、至る所で、しかも、データベース処理等の、かなり本質的な部分で使われています。


 自分でシグナルを送ることも、新しいシグナルを定義することもできるのですが、ここではDjangoの定義済みシグナルを受け取る例を、簡単に紹介します。
 イベント(シグナル)の通知を受ける側は、次のようにコールバック関数を登録します。

# -*- coding: utf-8 -*-
import django.dispatch.dispatcher
import django.db.models.signals
from myproject.myapp.models import Page

django.dispatch.dispatcher.connect(
    # イベントの通知を受ける関数
    receiver = myproject.myapp.receivers.page_saved,
    # イベントを識別するための「シグナル・オブジェクト」
    signal = django.db.models.signals.post_save,
    # イベントの発生元となるタイプ
    sender = Page
)


 上のスニペットでは、Pageオブジェクトがデータベースに保存されたタイミングで、myproject.myapp.receivers.page_savedというユーザ定義関数を呼び出すように、イベント通知の登録を行っています。
 イベント発生時に呼び出される関数は、次のような感じで記述します。

# -*- coding: utf-8 -*-
# myproject.myapp.receiversモジュール

def post_saved(signal, sender, instance, **kwds):
    # 保存されたモデル(instance)を使って何かをする
    pass


 post_savedがイベント発生時に呼び出される関数です。引数に与えられるのは、順番にシグナル・オブジェクト(signal)、イベント発生元(sender)、キーワード引数(この場合は、保存されたモデルのインスタンス)です。
 このコールバック関数を登録した時に、"signal = django.db.models.signals.post_save", "sender = Page"のように、イベント(シグナル)とイベント発生元を明示的に指定してるので、この場合は、Pageオブジェクトが保存された後にしか呼び出されません。
 逆に言うならば、signalやsenderを指定しないことにより、すべてのシグナルを受け取るような関数や、あらゆる発生源からのシグナルを処理する関数を登録できるということです。


 このシグナルの機能を使って何ができるかというと、多分、次のようなことができます。

  • リクエストを処理する前/した後に、リソースの初期化/解放をする
  • モデルが保存される/された後に何かする。
    • メールを送信する。
    • 集計処理を行う。集計処理を行ってグラフの画像を更新する。
    • 外部のタスク・スケジューラーにタスクを追加する。
    • Commetサーバーに待機させているコネクションにイベントを通知する。
  • manage.py syncdbが呼ばれた後に、何かする。


 データベース関連の処理は、データベース・トリガーを使えば同様の処理を実現できるでしょうが、データベース外部のリソースを使用するようなケースでは、アプリケーション側で処理した方が楽になるかもしれません。
 実際に何ができるかは、django.dispatchモジュールのコードや、django.core.signals、django.db.models.signalsに登録されているシグナルを確認して、考えてみて下さい。;-)


 Matt Riggottさんによる解説記事には、イベント発生時にメールを送るという具体例や、関連リンクの紹介があり、django.dispatchを理解する助けになりました。


Django signals
http://www.mercurytide.com/knowledge/white-papers/django-signals


 次のエントリでは、django.dispatchとHyperEstraierで、サイトの全文検索機能を提供する例を紹介します。

*1:[http://code.djangoproject.com/wiki/Signals:title=ここに書いてありました]