首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >覆盖Django的级联删除行为有哪些选项?

覆盖Django的级联删除行为有哪些选项?
EN

Stack Overflow用户
提问于 2010-03-19 13:56:07
回答 3查看 50.6K关注 0票数 68

Django模型通常可以很好地处理删除时的级联行为(在某种程度上,它适用于本地不支持它的数据库)。

然而,我正在努力寻找在不合适的地方覆盖这种行为的最好方法,例如在以下场景中:

  • ON DELETE RESTRICT (即,如果对象有子记录,则禁止删除该对象)
  • ON delete SET NULL (即,不删除子记录,而是将其父键设置为NULL,以便在删除记录(例如,删除上传的图像文件)时中断
  • 其他相关数据)

以下是我所知道的实现这些目标的潜在方法:

  • 覆盖模型的delete()方法。虽然这种方法是有效的,但是当通过QuerySet删除记录时,它就会被绕过。此外,每个模型的delete()都必须被覆盖,以确保Django的代码永远不会被调用,并且super()不能被调用,因为它可能使用QuerySet删除子对象。
  • 使用信号。当直接删除模型或通过QuerySet删除时,这似乎是理想的调用。但是,没有可能阻止删除子对象,因此无法在CASCADE RESTRICT或SET NULL上实现它。
  • 使用一个数据库引擎来正确处理这一点(在这种情况下Django做了什么?)
  • 一直等到Django支持它(并在此之前一直存在错误...)

第一种选择似乎是唯一可行的,但它很丑陋,会把婴儿和洗手水一起扔掉,而且当添加新的模型/关系时,可能会丢失一些东西。

我是不是遗漏了什么?有什么建议吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-12-09 05:04:08

对于那些遇到这个问题的人,请注意,Django 1.3中现在有一个内置的解决方案。

请参阅文档django.db.models.ForeignKey.on_delete中的详细信息,感谢代码片段网站的编辑指出这一点。

最简单的场景就是在你的模型FK字段定义中添加:

on_delete=models.SET_NULL
票数 96
EN

Stack Overflow用户

发布于 2010-03-19 14:35:39

Django只模拟级联行为。

根据Django用户组中的discussion的说法,最合适的解决方案是:

删除时重复限制方案-在删除前手动执行obj.rel_set.clear() (对于每个相关模型)删除时重复限制方案-在obj.delete()之前手动检查is obj.rel_set

  • ()。
票数 7
EN

Stack Overflow用户

发布于 2010-04-01 10:24:29

好的,以下是我已经决定的解决方案,尽管它远不能令人满意。

我为我的所有模型添加了一个抽象基类:

class MyModel(models.Model):
    class Meta:
        abstract = True

    def pre_delete_handler(self):
        pass

信号处理程序捕获此模型的子类的任何pre_delete事件:

def pre_delete_handler(sender, instance, **kwargs):
    if isinstance(instance, MyModel):
        instance.pre_delete_handler()
models.signals.pre_delete.connect(pre_delete_handler)

在我的每个模型中,如果存在子记录,我通过从pre_delete_handler方法抛出异常来模拟任何"ON DELETE RESTRICT“关系。

class RelatedRecordsExist(Exception): pass

class SomeModel(MyModel):
    ...
    def pre_delete_handler(self):
        if children.count(): 
            raise RelatedRecordsExist("SomeModel has child records!")

这将在修改任何数据之前中止删除。

不幸的是,不可能更新pre_delete信号中的任何数据(例如,模拟ON DELETE SET NULL),因为在发送信号之前,Django已经生成了要删除的对象列表。Django这样做是为了避免陷入循环引用,并防止不必要地多次发出对象信号。

确保可以执行删除现在是调用代码的责任。为了帮助实现这一点,每个模型都有一个prepare_delete()方法,该方法负责通过self.related_set.clear()或类似方法将关键点设置为NULL

class MyModel(models.Model):
    ...
    def prepare_delete(self):
        pass

为了避免在views.pymodels.py中更改太多代码,在MyModel上重写了delete()方法以调用prepare_delete()

class MyModel(models.Model):
    ...
    def delete(self):
        self.prepare_delete()
        super(MyModel, self).delete()

这意味着通过obj.delete()显式调用的任何删除操作都将按预期工作,但如果删除操作是从相关对象级联完成的,或者是通过queryset.delete()完成的,并且调用代码没有确保在必要时所有链接都被断开,则pre_delete_handler将抛出异常。

最后,我向在post_delete信号上调用的模型添加了一个类似的post_delete_handler方法,并让模型清除任何其他数据(例如,删除ImageField的文件)。

class MyModel(models.Model):
     ...

    def post_delete_handler(self):
        pass

def post_delete_handler(sender, instance, **kwargs):
    if isinstance(instance, MyModel):
        instance.post_delete_handler()
models.signals.post_delete.connect(post_delete_handler)

我希望这对某些人有帮助,并且代码可以被重新线程化成更有用的东西,而不会有太多麻烦。

任何关于如何改进这一点的建议都非常受欢迎。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2475249

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档