[Fixed]-Django cascade delete on reverse foreign keys

13👍

There is a very delicate implementation point, that I thought I should add to this discussion.

Let’s say we have two models, one of which references the other one by a foreign key, as in:

class A(models.Model):
    x = models.IntegerField()

class B(models.Model):
    a = models.ForeignKey(A, null=True, blank=True)

Now if we delete an entry of A, the cascading behavior will cause the reference in B to be deleted as well.

So far, so good. Now we want to reverse this behavior. The obvious way as people have mentioned is to use the signals emitted during delete, so we go:

def delete_reverse(sender, **kwargs):
    if kwargs['instance'].a:
        kwargs['instance'].a.delete()

post_delete.connect(delete_reverse, sender=B)

This seems to be perfect. It even works! If we delete a B entry, the corresponding A will also be deleted.

The PROBLEM is that this has a circular behavior which causes an exception: If we delete an item of A, because of the default cascading behavior (which we want to keep), the corresponding item of B will also be deleted, which will cause the delete_reverse to be called, which tries to delete an already deleted item!

The trick is, you need EXCEPTION HANDLING for proper implementation of reverse cascading:

def delete_reverse(sender, **kwargs):
    try:
        if kwargs['instance'].a:
            kwargs['instance'].a.delete()
    except:
        pass

This code will work either way. I hope it helps some folks.

👤Ali B

2👍

I don’t think the feature you are looking at is an ORM or database concept. You just want to execute a callback when something is deleted.

So use the post_delete signal and add you callback handler there

from django.db.models.signals import post_delete
from django.dispatch import receiver
from myapp.models import MyModel

@receiver(post_delete, sender=MyModel)
def my_post_delete_callback(sender, **kwargs):
    #Sender is the model which when deleted should trigger this action
    #Do stuff like delete other things you want to delete
    #The object just deleted can be accessed as kwargs[instance]

Leave a comment