Django级联删除反向外键

时间:2021-04-18 18:06:22

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

Django展示了如何在文档中设置或覆盖带有外键的级联删除。

model = models.ForeignKey(MyModel, null = True, on_delete = models.SET_NULL)

But what if we wanted this effect the other way around? What if we want the deletion of the fk model to result in the deletion of this model?

但是如果我们想要这种效果反过来呢?如果我们希望fk模型的删除导致这个模型的删除怎么办?

Thanks

谢谢

2 个解决方案

#1


9  

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.

现在,如果我们删除A的条目,级联行为将会导致B中的引用被删除。

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.

这看起来很完美。它甚至工作!如果我们删除一个B条目,对应的a也会被删除。

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!

问题是,这一个圆形的行为导致一个例外:如果我们删除一个条目,因为默认的级联行为(我们希望),B的相应的项目也将被删除,这将导致delete_reverse,试图删除一个已经删除的项!

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.

这段代码将以任何一种方式工作。我希望它能帮助一些人。

#2


0  

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.

我认为您看到的特性不是ORM或数据库概念。当某个东西被删除时,您只想执行回调。

So use the post_delete signal and add you callback handler there

所以使用post_delete信号并在那里添加回调处理程序

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]

#1


9  

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.

现在,如果我们删除A的条目,级联行为将会导致B中的引用被删除。

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.

这看起来很完美。它甚至工作!如果我们删除一个B条目,对应的a也会被删除。

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!

问题是,这一个圆形的行为导致一个例外:如果我们删除一个条目,因为默认的级联行为(我们希望),B的相应的项目也将被删除,这将导致delete_reverse,试图删除一个已经删除的项!

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.

这段代码将以任何一种方式工作。我希望它能帮助一些人。

#2


0  

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.

我认为您看到的特性不是ORM或数据库概念。当某个东西被删除时,您只想执行回调。

So use the post_delete signal and add you callback handler there

所以使用post_delete信号并在那里添加回调处理程序

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]