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

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

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

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

谢谢

【问题讨论】:

    标签: python mysql django


    【解决方案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 信号”)。我没有意识到批量删除的实现方式不同。我正在删除原始评论以确保它不会混淆。
    猜你喜欢
    • 2020-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-23
    • 2011-02-05
    • 1970-01-01
    • 2011-02-24
    • 2018-04-13
    相关资源
    最近更新 更多