Django级联删除反向外键

Posted

技术标签:

【中文标题】Django级联删除反向外键【英文标题】:Django cascade delete on reverse foreign keys 【发布时间】:2012-09-21 05:51:18 【问题描述】:

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

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

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

谢谢

【问题讨论】:

【参考方案1】:

有一个非常微妙的实现点,我想我应该添加到这个讨论中。

假设我们有两个模型,其中一个通过外键引用另一个,如下所示:

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

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

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

到目前为止,一切都很好。现在我们要扭转这种行为。人们提到的明显方法是使用删除期间发出的信号,所以我们去:

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

post_delete.connect(delete_reverse, sender=B)

这似乎是完美的。它甚至可以工作!如果我们删除一个 B 条目,对应的 A 也会被删除。

问题是这有一个循环行为导致异常:如果我们删除A的一个项目,由于默认的级联行为(我们想要保留),B的相应项目也将被删除,这将导致 delete_reverse 被调用,它会尝试删除已删除的项目!

诀窍是,您需要异常处理才能正确实现反向级联:

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

此代码将适用于任何一种方式。我希望它可以帮助一些人。

【讨论】:

顺便说一句,这可能不会导致无限递归,对吧? 为什么不首先使用 pre_delete 信号?【参考方案2】:

我不认为您正在查看的功能是 ORM 或数据库概念。您只想在删除某些内容时执行回调。

所以使用post_delete signal 并在那里添加你的回调处理程序

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]

【讨论】:

@claytond 虽然您对 delete 没有被调用是正确的,正如文档中引用的那样,但我找不到提到不能保证调用 post_delete 信号。事实上,我只是在 django 2.0.4 中运行了一个简单的测试,显示 MyModel.delete 确实没有被调用,但是我的 pre_deletepost_delete 信号回调被调用了 @Andy 你是对的。我遇到了批量update 的问题(“update() 在 SQL 级别进行更新,因此不会在模型上调用任何 save() 方法,也不会发出 pre_save 或 post_save 信号”)。我没有意识到批量删除的实现方式不同。我正在删除原始评论以确保它不会混淆。

以上是关于Django级联删除反向外键的主要内容,如果未能解决你的问题,请参考以下文章

mysql外键级联更新删除

SQL Server - 使用递归外键级联删除

Django级联删除反向外键

依赖外键级联不好吗?

MySQL约束 主键约束丨唯一约束丨非空约束丨外键级联

为啥 Django 对外键进行级联删除?