cloverrose's blog

Python, Machine learning, Emacs, CI/CD, Webアプリなど

Djangoのm2m_changedシグナルを使ってみた

mysite.myapp.models.py

from django.db import models
from django.db.models.signals import post_save, m2m_changed

class Topping(models.Model):
    name = models.CharField(max_length=10)


class Pizza(models.Model):
    toppings = models.ManyToManyField(Topping)


def change_m2m(sender, instance, action,
               reverse, model, pk_set, using, **kwargs):
    print 'sender  :' + str(sender)
    print 'instance:' + str(instance)
    print 'action  :' + str(action)
    print 'reverse :' + str(reverse)
    print 'model   :' + str(model)
    print 'pk_set  :' + str(pk_set)
    print 'using   :' + str(using)

m2m_changed.connect(change_m2m, sender=Pizza.toppings.through)


python manage.py shell

>>> from mysite.myapp.models import Pizza, Topping
>>> p = Pizza.objects.create()
>>> t = Topping.objects.create(name='ham5')
>>> p.toppings.add(t)
sender  :<class 'mysite.myapp.models.Pizza_toppings'>
instance:Pizza object
action  :pre_add
reverse :False
model   :<class 'mysite.myapp.models.Topping'>
pk_set  :set([5])
using   :default
sender  :<class 'mysite.myapp.models.Pizza_toppings'>
instance:Pizza object
action  :post_add
reverse :False
model   :<class 'mysite.myapp.models.Topping'>
pk_set  :set([5])
using   :default

注意点

Pizza p1のtoppingsをt1からt2へ変更する作業を
AdminサイトでPizzaのページで行った場合、一旦クリアしてからt2を追加という流れになる。
つまり、t1がp1のリレーションから外れたことを感知できない

pre_clear
post_clear
pre_add t2.pk
post_add t2.pk
pre_clear
post_clear

となる
最後のclearが謎

注意点

post_saveシグナルをキャッチしてその中で、
リレーションの追加や削除をする方法を使っていたが、いかんせんうまくいかない。

よくよく調べてみると、adminサイトでリレーションをいじれるようにしていると(editable=Falseにしていないと)
saveのたびにm2m_change.post_clearも呼ばれてしまい、
せっかく設定したリレーションが無効化されてしまっていた