Django cascade delete on reverse foreign keys

前端 未结 2 1917
一个人的身影
一个人的身影 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: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.

提交回复
热议问题