简体   繁体   English

Django级联删除反向外键

[英]Django cascade delete on reverse foreign keys

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 谢谢

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

The trick is, you need EXCEPTION HANDLING for proper implementation of reverse cascading: 诀窍是,您需要EXCEPTION HANDLING才能正确实现反向级联:

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. 我希望它可以帮助一些人。

I don't think the feature you are looking at is an ORM or database concept. 我认为您所看到的功能不是ORM或数据库概念。 You just want to execute a callback when something is deleted. 您只想在删除某些内容时执行回调。

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]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM