Djangoシグナル登録の簡単なまとめ

#Djangoのシグナルについて
##はじめに
Djangoで特定のイベント発生時にシグナルを受信することができます。

HTTPリクエストを送信する時に呼ばれる`request_started`や、HTTPリクエストの処理が終了した時に呼ばれる`request_finished`がそれにあたります。

##Receiver関数の登録
レシーバ関数を登録する方法は２通り
・Signal.connect
・receiverデコレータ

##connect
Django1.7.1

```py3
connect(self, receiver, sender=None, weak=True, dispatch_uid=None)
```

Django1.7.1のconnect関数になります。
各パラメータは、
######**receiver**
    function or instance method
    シグナル受信時に実行されるオブジェクトになります。
######**sender**
    特定のsenderからのみシグナルを受信する場合に設定します。
######**weak**
    ここがTrueならreceiverが弱い参照で保持されます。
######**dispatch_uid**
    特定のシグナルに対して重複したモジュールを登録する場合に設定します。
    設定する場合は、文字列を指定すれば問題ないです。

Djangoのauthモジュールの例

```py3
#user loginのシグナル定義
#django.contrib.auth.signals
user_logged_in = Signal(providing_args=['request', 'user'])

#receiverファンクション
def login_callback(sender, **kwargs):
    #Signal定義のproviding_argsに定義されているオブジェクトを取得できる
    request = kwargs['request']
    user = kwargs['user']

#connect
user_logged_in.connect(login_callback)
```

これでログイン後に***login_callback***が呼ばれます。
実際に***user_logged_in***がシグナル送信している箇所をみてみます。

```py3
    ##django.contrib.auth.__init__
    request.session[SESSION_KEY] = user.pk
    request.session[BACKEND_SESSION_KEY] = user.backend
    request.session[HASH_SESSION_KEY] = session_auth_hash
    if hasattr(request, 'user'):
        request.user = user
    rotate_token(request)
    user_logged_in.send(sender=user.__class__, request=request, user=user)
```

sendメソッドでsender、request,userを設定しているのがわかります。

##receiverデコレータ
レシーバ関数を登録するもう一つの方法は、receiverデコレータを使う方法です。

```py3
@receiver(signal, sender=myModel)
def test_callback(sender, **kwargs):
    pass
```

こんな感じで定義します。
receiverの中を見てみます。

```py3
#django.dispatch.dispatcher
def receiver(signal, **kwargs):
    def _decorator(func):
        if isinstance(signal, (list, tuple)):
            for s in signal:
                s.connect(func, **kwargs)
        else:
            signal.connect(func, **kwargs)
        return func
    return _decorator
```

見てわかる通りconnectを呼んでいます。
複数のシグナルを登録することも可能です。

######デコレータ
補足として***receiver***デコレータの挙動をこの自作receiverを参考に見てみます。

```py3
#decodeco.py
def receiver(signal, **kwargs):
    def _deco(func): #func=test
        print('receiver %s' % kwargs['sender'])
        print(signal) #signal=sample
        print(func)
        return func
    return _deco

def sample():
    print('sample')

@receiver(sample, sender='sender_mymodel')
def test(sender, **kwargs):
    print('test')

if __name__ == '__main__':
    test(1)
```

これを実行してみます。

```bash
$ python3 decodeco.py 
receiver sender_mymodel
<function sample at 0x7f63129f3050>
<function test at 0x7f63129f3170>
test
```

デコレータで展開さているのが少しわかると思います。

実際にconnectで登録した***user_logged_in***を***receiver***に変更してみます。

```py3
@receiver(user_logged_in, sender=myModel)
def login_callback(sender, **kwargs):
    request = kwargs['request']
    user = kwargs['user']
```

さらにこの***receiver***をわかりやすくしてみます。

```py3
#django.dispatch.dispatcher.receiverをlogin_callbackで展開した
def receiver(user_logged_in, {'sender': myModel}):
    def _decorator(login_callback):
        user_logged_in.connect(login_callback, {'sender': myModel})
        return login_callback
    return _decorator
```

簡単に書くとこんな感じでしょうか。
***connect***、***receiver***で登録するのもどちらも同じ様なものなので、用途によって使い分けるといいと思います。

##最後に
***connect***の中を見てみます。

```py3
    #django.dispatch.dispatcher
    #長いので、途中省略してます
    def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
        #ここで各レシーバのキーを作成します
        if dispatch_uid:
            lookup_key = (dispatch_uid, _make_id(sender))
        else:
            lookup_key = (_make_id(receiver), _make_id(sender))

        if weak:
            ref = weakref.ref
            receiver_object = receiver
            # Check for bound methods
            if hasattr(receiver, '__self__') and hasattr(receiver, '__func__'):
                ref = WeakMethod
                receiver_object = receiver.__self__
            if sys.version_info >= (3, 4):
                receiver = ref(receiver)
                weakref.finalize(receiver_object, self._remove_receiver)
            else:
                receiver = ref(receiver, self._remove_receiver)

        with self.lock:
            self._clear_dead_receivers()
            for r_key, _ in self.receivers:
                if r_key == lookup_key:
                    break
            else:
                #ここでレシーバ関数を登録
                self.receivers.append((lookup_key, receiver))
            self.sender_receivers_cache.clear()

```

***connect***の中を見ると、弱参照の話も必要ですが長くなるので今回は省略します。
最後の***self.receivers.append((lookup_key, recever))***でレシーバを登録しているのがわかると思います。

これでレシーバ登録までの流れは終わりです。
機会があれば***send***や***_live_receivers***もまとめられればいいなと思います。

間違っている箇所等ありましたら指摘いただければ助かります。