如何使用不会将删除级联到其子级的 ForeignKeys 创建 Django 模型?

Posted

技术标签:

【中文标题】如何使用不会将删除级联到其子级的 ForeignKeys 创建 Django 模型?【英文标题】:How do I create a Django model with ForeignKeys which does not cascade deletes to its children? 【发布时间】:2010-11-03 14:48:16 【问题描述】:

我的一个具有ForeignKey 的模型实际上是其他表上的 mysql 视图。我遇到的问题是,当我从这些表中删除数据时,Django,如"deleting objects" documentation...中所述。

当 Django 删除一个对象时,它 模拟 SQL 的行为 对 DELETE CASCADE 的约束——在 换句话说,任何具有 指向对象的外键 被删除将连同被删除 它。

...尝试从我的视图中删除行,这当然不能,因此会引发错误:

mysql_exceptions.OperationalError '>=(1395, "Can not delete from join view 'my_db.my_mysql_view'"'

有没有办法在模型上指定ForeignKey 约束,这将为我提供所有 Django 魔法,但不会级联删除?或者,有没有办法让 MySQL 忽略从我的视图中删除一行的命令而不是引发错误?

【问题讨论】:

【参考方案1】:

Django 1.3a1 及以上版本通过 ForeignKeyon_delete 参数支持此功能。

以下示例在删除外键时设置字段NULL。有关更多选项,请参阅documentation。

user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)

【讨论】:

【参考方案2】:

回复:http://code.djangoproject.com/ticket/7539

从 2010 年 6 月的 Django 1.2.1 开始没有关注。我想我们需要“注意那个空间”。

【讨论】:

【参考方案3】:

仅供参考 - django 源代码库中存在此功能请求http://code.djangoproject.com/ticket/7539。看来这个话题正在引起一些关注。希望它将包含在未来的 Django 版本中。

票证包括 Django 核心的补丁,用于实现 models.ForeignKey(...) 的“on_delete”可选参数,让您指定删除指向的模型时会发生什么,包括关闭默认的 ON DELETE CASCADE 行为.

【讨论】:

是的 - 它已经发布到 1.3 -> docs.djangoproject.com/en/dev/releases/1.3-alpha-1/#configurable-delete-cascade 感谢@stevejalim 的更新。好消息。我想要这个功能很久了。【参考方案4】:

Harold 的回答为我指明了正确的方向。这是我实现它的方式的草图(在法国遗留数据库上,因此命名约定有点奇怪):

class Factures(models.Model):
    idFacture = models.IntegerField(primary_key=True)
    idLettrage = models.ForeignKey('Lettrage', db_column='idLettrage', null=True, blank=True)

class Paiements(models.Model):
    idPaiement = models.IntegerField(primary_key=True)
    idLettrage = models.ForeignKey('Lettrage', db_column='idLettrage', null=True, blank=True)

class Lettrage(models.Model):
    idLettrage = models.IntegerField(primary_key=True)

    def delete(self):
        """Dettaches factures and paiements from current lettre before deleting"""
        self.factures_set.clear()
        self.paiements_set.clear()
        super(Lettrage, self).delete()

【讨论】:

on_delete=models.SET_NULL : docs.djangoproject.com/en/1.4/ref/models/fields/#foreignkey(sinze 1.3 版本) 为什么SET_NULL 会提供帮助? UPDATE将外键设置为 NULL 的视图仍然会引发异常。 models.DO_NOTHING 确实有帮助。 添加null=True 后仍然出现错误。按照下面的 zourtney 建议添加 on_delete=models.SET_NULL 就可以了。【参考方案5】:

一种方法是在删除之前调用 clear 方法,documentation here 这基本上“清除”了关系。 一个问题认为:它本身不是自动的。您可以选择:每次不想级联时调用它,或者在每次删除之前使用 pre_delete 信号发送清除,当然当您想要删除时它会给您带来问题 - 级联。

或者你可以为 django 社区做贡献并添加要删除的关键字参数,也许它会在 django 1.3 中?:D

【讨论】:

【参考方案6】:

Django 的 ForeignKey 管理器有一个名为 clear() 的方法,可以从相关对象集中删除所有对象。首先调用它,然后删除您的对象应该可以工作。依赖对象的外键设置为 None(如果您的模型允许)。

这里有一个简短的描述: http://docs.djangoproject.com/en/dev/topics/db/queries/#following-relationships-backward

【讨论】:

【参考方案7】:

嗯,看看删除方法

def delete(self):
    assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname)

    # Find all the objects than need to be deleted.
    seen_objs = CollectedObjects()
    self._collect_sub_objects(seen_objs)

    # Actually delete the objects.
    delete_objects(seen_objs)

我想说覆盖删除就足够了......未经测试的代码将是

def delete(self):
    assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname)

    # Find all the objects than need to be deleted.
    seen_objs = CollectedObjects()
    seen_objs.add(model=self.__class__, pk=self.pk, obj=self, parent_model=None)

    # Actually delete the objects.
    delete_objects(seen_objs)

【讨论】:

(是的,这有点乱;我想说 ForeignKey 应该有参数...在 Django 票务系统中为它填写一个功能请求;))无论如何,对于您的看法,看看这个错误/补丁:code.djangoproject.com/ticket/10829 忘记删除查询集。

以上是关于如何使用不会将删除级联到其子级的 ForeignKeys 创建 Django 模型?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 enumerable: false 不会级联到 TypeScript 中的继承类?

EasyNVR级联到上级平台通道数量显示不正确是什么原因?

休眠:分离没有级联到集合

EasyNVR级联到EasyGBS国标平台,上级通道状态频繁切换的原因分析与解决

EasyCVR级联到上级平台不显示是什么原因?

如何配置海康联网网关上级域,通过国标GB28181级联到EasyCVR?