如何覆盖模型上的 delete() 并让它仍然与相关的删除一起使用

Posted

技术标签:

【中文标题】如何覆盖模型上的 delete() 并让它仍然与相关的删除一起使用【英文标题】:How do I override delete() on a model and have it still work with related deletes 【发布时间】:2010-12-04 20:00:36 【问题描述】:

我遇到了问题,因为我正在使用 some_widget_instance.delete() 删除小部件。我还有一个名为 WidgetFile 的模型,它带有一个覆盖 delete() 方法,这样当一个 WidgetFile 被删除时,我就可以从我的硬盘上删除文件。我遇到的问题是,如果我删除一个小部件,并且它有与之相关的 WidgetFiles,如下所示:

class WidgetFile(models.Model):

    widget = models.ForeignKey(Widget)

好吧,当我删除那个 Widget 时,它的 WidgetFiles 被删除了,但是 delete() 方法并没有触发并做我额外的硬盘驱动器的事情。非常感谢任何帮助。

【问题讨论】:

出现这个问题的原因是当一个小部件被删除时,它不会触发它的每个依赖项(具有对其的外键引用的类)的 delete() 方法。它只是从数据库中删除相关对象。这使它更有效率,但显然会导致这样的问题。 【参考方案1】:

我也在做同样的事情,并注意到 Django 文档中有一个你应该考虑的块。

Overriding predefined model methods

覆盖删除 请注意,当使用 QuerySet 批量删除对象时,不一定会调用对象的 delete() 方法。为确保执行自定义删除逻辑,您可以使用 pre_delete 和/或 post_delete 信号。

这意味着你的 sn-p 不会总是做你想做的事。使用Signals 是处理删除的更好选择。

我选择了以下内容:

import shutil
from django.db.models.signals import pre_delete 
from django.dispatch import receiver

@receiver(pre_delete)
def delete_repo(sender, instance, **kwargs):
    if sender == Set:
        shutil.rmtree(instance.repo)

【讨论】:

什么是Set?您正在监控删除的模型?【参考方案2】:

我想通了。我只是把它放在那个 Widget 模型上:

def delete(self):
    files = WidgetFile.objects.filter(widget=self)
    if files:
        for file in files:
            file.delete()
    super(Widget, self).delete()

这触发了每个相关对象上必要的 delete() 方法,从而触发了我的自定义文件删除代码。是的,它的数据库成本更高,但是当您尝试删除硬盘上的文件时,多打几次数据库并不是什么大开销。

【讨论】:

目前尚不清楚为什么您认为 Celery 或 cron 不会遇到类似的情况,即被删除的文件可能已经被另一个进程打开以进行读/写操作。无论哪种情况,您都必须编写代码来处理特殊情况。 我删除了那个位... 4.5 年前我认为这可能是个好主意,但我不太确定为什么。【参考方案3】:

在删除之前使用clear(),从相关对象集中删除所有对象。

见django-following-relationships-backward

示例:

group.link_set.clear() 
group.delete() 

【讨论】:

【参考方案4】:

这似乎只有在一个 Widget 准确地连接到一个 WidgetFile 时才有意义。在这种情况下,您应该使用OneToOneField

来自On-to-one examples:

# Delete the restaurant; the waiter should also be removed
>>> r = Restaurant.objects.get(pk=1)
>>> r.delete()

【讨论】:

确实如此,但 Django 对所有服务员进行数据库级别的批量删除,而不触发他们的每个删除方法,这更便宜,但也不太传统。【参考方案5】:

只是为了解决这个问题:pre-deletesignal。 (绝不意味着没有实际的解决方案。)

【讨论】:

【参考方案6】:

它应该看起来像 django 网站上描述的那样:

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()
    def save(self, *args, **kwargs):
        do_something()
        super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
        do_something_else()

http://docs.djangoproject.com/en/dev/topics/db/models/#overriding-predefined-model-methods

你忘了传递一些参数

【讨论】:

【参考方案7】:

some_widget_instanceWidgetWidgetFile 的实例吗?因为如果它是Widget 的一个实例,它将无法获得您的自定义delete() 函数,该函数位于WidgetFile 类中。

【讨论】:

【参考方案8】:

从 Django 1.9 开始,如果您只为字段定义 on_delete=models.CASCADE,它将在删除时删除所有相关对象。

【讨论】:

这是不对的。问题不在于级联删除。这是为了确保调用相关的delete 方法。

以上是关于如何覆盖模型上的 delete() 并让它仍然与相关的删除一起使用的主要内容,如果未能解决你的问题,请参考以下文章

你好我问一个问题是3Dsmax人物模型如何把骨骼绑定到人物模型里面呢并让它一起行走起来请懂的教我一下

如何在两个 Text 组件之间放置一个 TextInput 并让它包装得很好?

我应该如何在 UITableView 下放置一个按钮并让它随着表格的增长而向下移动?

您如何将 2D 矩阵表示为输入状态,并让它选择它认为对该状态最佳操作的行的索引?

EF Code First:如何将虚拟集合设为私有,同时仍然让它正确创建我的数据库模型?

如何安装 OpenCV 'samples' 库并让它在 armhf 上工作?