diadia

興味があることをやってみる。自分のメモを残しておきます。

django signal(シグナル)について

シグナル関連記事

post_save,pre_saveの使い分けについて[仮説]

本題

私の場合djangoがはじめてのwebフレームワークで、djangoの仕組みを理解することは かなりハードルが高い。それはwebアプリケーションの仕組みの理解やフレームワークの概念の理解、フレームワークの運用上の知識、pythonの知識、サーバーの知識をいっぺんに求められることに起因していると思われる。そしてシグナルもよくわからない概念だったが少しずつ分かってきた。ドキュメントを読んでもピンとこないのはその概念をイメージできないことが原因だと思われる。日本人の誰かの概念のイメージに役立てば嬉しい。

シグナルについての参考資料

https://code-examples.net/ja/docs/django~2.0/topics/signals
受信と受信機機能の接続というものがあるらしい。ここでpre_save,post_save,m2m_changedを使う。シグナルの定義とか送信という別のテーマがある。これについてはpre_save,post_save,m2m_changedを使わない...?

m2m_changedについて

https://docs.djangoproject.com/ja/2.1/ref/signals/#m2m-changed

他のものと違ってactionという引数がある。受信に記載する。 senderにthrough等を追加して書かないといけない。
あと理解している人には当たり前だが、m2m_changedを使う場面でpost_saveを使っても期待通りの挙動は得られない。ManyToManyFieldにおいてシグナルを扱いたい場合はm2m_changedを使うこと。save()のあとにシグナルを発するからと言ってpost_savedでは上手くいかない。

シグナルはどこで記述するのか?

models.pyに記述する。 今の所わかっていること シグナルの定義 シグナルの発信 シグナルのレシーブ(コネクション?) これらがある。 これらを書く場所 signals.pyにシグナルの定義 views.pyにシグナルの発信 models.pyにレシーバの定義 コネクト 書き方 user_login=Signal(providing_args=["instance","request"]) user_login.send(sender=self.__class__, instance=user,request=request) def user_login_reciever(sender,instance,request,*args,**kwargs) user_login.connect(user_login_reciever)

シグナルの使い方少しわかった

シグナルのsend(),connect()について

send(),connect()はシグナルクラスのインスタンスメソッドである。だからsend(),connect()を使うためにはインスタンスを作らなければならない。

シグナルクラスのインスタンスを作る方法

以下のようにclass Signalを呼び出してインスタンスを作成する。

from django.dispatch import Signal
signal_instance = Signal(providing_args=["任意","にんい"])

connect()について

connect()の引数にはレシーバーを入れる。レシーバーはどんな動きをしてほしいかの内容をdefを用いて書く。

 レシーバーの定義に関しては引数に関する条件がある。引数はsenderと**kwargsである。

send()について

send()メソッドではsignalインスタンスで定義したproviding_argsの内容(これがkey)に対する値(value)を引数に設定する。またsenderもvalueを決めてあげる。send()を使う文脈だが、views.py内でシグナルを送りたいタイミングでsend()文を挿入する。

signal_instance.send(sender="ここを設定",任意="ここを設定",にんい="ここを設定")

pre_save(),post_save(),m2m_changedのシグナルの立ち位置について

今までpre_save(),post_save()についてシグナルの定義をしないで使っていたけど、よく考えればおかしい。どうして定義してインスタンスを作らなくても期待通りの動きをしてくれたのか?これについては、djangoの公式ドキュメントに記載されていた。
ドキュメント:https://docs.djangoproject.com/ja/2.1/topics/signals/

Django provides a set of built-in signals that let user code get notified by Django itself of certain actions. These include some useful notifications: django.db.models.signals.pre_save & django.db.models.signals.post_save Sent before or after a model's save() method is called. django.db.models.signals.pre_delete & django.db.models.signals.post_delete Sent before or after a model's delete() method or queryset's delete() method is called. django.db.models.signals.m2m_changed Sent when a ManyToManyField on a model is changed. django.core.signals.request_started & django.core.signals.request_finished Sent when Django starts or finishes an HTTP request.

最初の一文のDjango provides a set of built-in signals that let user code get notified by Django itself of certain actions.は、できあいのシグナルをdjangoは準備してくれていることを意味していて、その下に続く文章はdjangoが準備してくれているシグナルを表している。

要するに?

djangoでシグナルの機能を実装する際に、まず期待する挙動がdjango自体が用意してくれた下記のsignalで対応できるか考える。

  • django.db.models.signals.pre_save
  • django.db.models.signals.post_save
  • django.db.models.signals.pre_delete
  • django.db.models.signals.post_delete
  • django.db.models.signals.m2m_changed
  • django.core.signals.request_started
  • django.core.signals.request_finished

これらのシグナルで実現できない場合は、自分自身でカスタムしたシグナルを作成することになる。それはつまり、この段階でシグナルの定義(インスタンスを作った)りする。

シグナルの各要素の役割

Signalの定義で何の要素を渡すかを定義する。sendメソッドを使ってシグナルの発射タイミングを規定する。レシーバーで送られたシグナルの要素を加工する(挙動を指定)。そしてconnectメソッドでレシーバーとシグナルをつなげる。