NOTICE: due to production environment constraints, I must stick to django-1.4
for the moment.
I've just made a test to see whether I can hook onto an event when ManyToMany changes.
I have a Group
model that holds several Item
objects. Whenever the items change in any group, I want to do something with concerned Group` instances.
from django.db import models
from django.db.models.signals import m2m_changed, post_delete, pre_delete
class Item(models.Model):
name = models.CharField(max_length=32)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=32)
items = models.ManyToManyField(to=Item)
def items_changed(signal, sender, action, instance, reverse, model, pk_set, using, **kwargs):
print str(signal)
print action, instance, reverse, pk_set
m2m_changed.connect(items_changed, sender=Group.items.through)
If I change the items
list on a Group
instance, I the m2m_changed
event is obviously fired.
>>> from sandbox.core.models import Group, Item
>>> item, created = Item.objects.get_or_create(name='f')
>>> g = Group.objects.get(pk=1)
>>> g.items.add(item)
pre_add Group object False set([5])
post_add Group object False set([5])
>>> g.items.remove(item)
pre_remove Group object False set([5])
post_remove Group object False set([5])
Now, when I remove the Item
end of the relation, nothing happens, however, the relation table entry is correctly deleted.
>>> item.delete()
I tried connecting to the delete signal of the m2m through table, but apparently, signals are not fired for the automatic relation tables.
Connected like this in my models.py
module:
def group_items_pre_delete(signal, sender, instance, using, **kwargs):
print 'pre_delete', instance
def group_items_post_delete(signal, sender, instance, using, **kwargs):
print 'post_delete', instance
pre_delete.connect(group_items_pre_delete, sender=Group.items.through)
post_delete.connect(group_items_post_delete, sender=Group.items.through)
The solution I have for now is to check manually for changes. The models are exposed through a REST API, so I can do this when an update request ends (but I would have liked it to be automated through the signals).
Are those "missing" signals fired in any newer version of Django?
This is a bug, not fixed yet: https://code.djangoproject.com/ticket/17688