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

Change History (0)

Note: See TracTickets for help on using tickets.
Back to Top