Django cascade delete on reverse foreign keys

前端 未结 2 1913
一个人的身影
一个人的身影 2021-02-13 12:55

Django shows how to set or override cascade deletes with foreign keys in their documents.

model = models.ForeignKey(MyModel, null = True, on_delete = models.SET         


        
相关标签:
2条回答
  • 2021-02-13 13:20

    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]
    
    0 讨论(0)
  • 2021-02-13 13:27

    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.

    0 讨论(0)
提交回复
热议问题