Opened 10 years ago
Last modified 10 years ago
#24738 closed Bug
Possible bug in m2m_changed signal when deleting related objects — at Initial Version
| Reported by: | No-0n3 | Owned by: | nobody |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | dev |
| Severity: | Normal | Keywords: | |
| Cc: | No-0n3 | Triage Stage: | Unreviewed |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
Hi,
Have observed that no m2m_changed signal is sent when a related object is deleted.
If I explicitly clear the relations with "clear()" or remove the relation with "remove()", m2m_changed is correctly sent but if I delete it without calling "clear()" or "remove()" on the m2m field no signal is sent to indicate a change in relationship.
Is this by design or should a m2m_changed signal be sent when a related object is deleted from the m2m table at deletion when the relations also are removed.
Right now I've done a workaround by catching the "pre_delete" signal on my model that explicitly calls "clear()" on the M2M field to trigger the signal and catching it with my other signal function when it gets a "post_clear" action in m2m_changed signal.
Here are my models and signals with my workaround:
from django.db import models
from django.db.models.signals import m2m_changed, pre_delete
from django.dispatch import receiver
from django.db.models import Count
# Models
class Type(models.Model):
name = models.CharField(max_length=200)
def __unicode__(self):
return unicode(self.name)
class Blacklist(models.Model):
source = models.URLField()
name = models.CharField(max_length=200)
date_added = models.DateField(auto_now_add=True)
last_update = models.DateField(auto_now=True)
types = models.ManyToManyField(Type)
def __unicode__(self):
return unicode(self.name)
class Record(models.Model):
blacklists = models.ManyToManyField(Blacklist)
ip = models.GenericIPAddressField(protocol='IPv4', unique=True)
def __unicode__(self):
return unicode(self.ip)
# Signal actions
@receiver(m2m_changed, sender=Record.blacklists.through)
def blacklists_changed(sender, **kwargs):
if kwargs['action'] == "post_clear":
if kwargs['reverse']:
Record.objects.annotate(count=Count('blacklists')).exclude(count__gt=0).delete()
@receiver(pre_delete, sender=Blacklist)
def blacklist_deleted(sender, **kwargs):
kwargs['instance'].record_set.clear()
If you comment out the blacklist_deleted and do a <Blacklist instance>.delete() no m2m_changed signal is sent at all.
BR / Isaac