Opened 8 years ago

Closed 8 years ago

#22594 closed Bug (fixed)

Content Type framework does not trigger Cascade delete

Reported by: JoseTomasTocino Owned by: nobody
Component: contrib.contenttypes Version: 1.6
Severity: Normal Keywords:
Cc: chk Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no


I've posted this question in StackOverflow ( and given it looks like a bug, I've decided to post it here.

Suppose the following models:

class DeltaCheck(models.Model):
    logs = generic.GenericRelation('Log')
    title = models.CharField(max_length=50)
    owner = models.ForeignKey(User)

class Log(models.Model):
    title = models.CharField(max_length=50)

    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')

If I create a DeltaCheck and a couple of Logs and then delete the DeltaCheck, the Logs are deleted as well:

In [7]: Log.objects.count()
Out[7]: 10

In [8]: DeltaCheck.objects.get().delete()

In [9]: Log.objects.count()
Out[9]: 0

BUT if I delete the User (the field owner), the DeltaCheck gets deleted BUT not the Logs, look:

In [14]: Log.objects.count()
Out[14]: 10

In [15]: DeltaCheck.objects.get().owner.delete()

In [16]: DeltaCheck.objects.all()
Out[16]: []

In [17]: Log.objects.count()
Out[17]: 10

Is that proper behavior? I don't think so.

Change History (5)

comment:1 Changed 8 years ago by JoseTomasTocino

Curiously, both pre_delete and post_delete signals are fired in the failing case... What happens in the middle?

Version 0, edited 8 years ago by JoseTomasTocino (next)

comment:2 Changed 8 years ago by chk

Cc: chk added

I'm able to reproduce the same issue with a similar database schema.

  • django 1.6.1
  • postgresql 9.1

As a workaround I've defined a pre_signal (but NOT empty as Jose says):

from django.db.models.signals import pre_delete
from django.dispatch import receiver

@receiver(pre_delete, sender=User)
def pre_delete_receiver(sender, instance, **kwargs):
    for delta in instance.deltachecks.all():

comment:3 Changed 8 years ago by Baptiste Mispelon

Triage Stage: UnreviewedAccepted
Type: UncategorizedBug


I can also reproduce this issue (and indeed, having an empty signal fixes the issue).

For reference, cascade deletion of generic foreign keys is documented and only works if you have a reverse GenericRelation [1] (last two paragraphs).

The key to this issue probably lies in the fast_delete method [2] (note for example the line 126 that explains why things behave differently if there is a signal listening or not).



comment:4 Changed 8 years ago by Anssi Kääriäinen

This is likely a duplicate of #22998 which has a patch. I am not going to close this yet as duplicate as I haven't verified that the fix in #22998 also fixes this issue.

comment:5 Changed 8 years ago by Nick Sandford

Resolution: fixed
Status: newclosed

I can confirm that #22998 fixes the issue, closing as fixed.

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