为啥我的模型的“on_delete=models.CASCADE”不生成级联外键约束?

Posted

技术标签:

【中文标题】为啥我的模型的“on_delete=models.CASCADE”不生成级联外键约束?【英文标题】:Why doesn't my model's "on_delete=models.CASCADE," generate a cascading foreign key constraint?为什么我的模型的“on_delete=models.CASCADE”不生成级联外键约束? 【发布时间】:2019-06-09 22:18:51 【问题描述】:

我正在使用 Django、Python 3.7 和 PostgreSQL 9.5。如何标记我的模型以生成级联外键约束?我目前在我的 models.py 文件中有这个...

class ArticleStat(models.Model):
    article = models.ForeignKey(Article, on_delete=models.CASCADE, )

当我在管理控制台中运行make migrations my_project 时,它会生成一个包含此文件的文件...

    migrations.CreateModel(
        name='ArticleStat',
        fields=[
            ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
            ...
            ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='my_project.Article')),
        ],
    ),

但是,当我运行迁移(使用migrate my_project 0001)时,生成的外键不包含级联删除约束。这就是 PostgreSQL 中的描述......

"my_project_articlesta_article_id_a02a5add_fk_my_project" FOREIGN KEY (article_id) REFERENCES my_project_article(id) DEFERRABLE INITIALLY DEFERRED

我怎样才能让我的 models.py 文件输出级联删除外键约束?

【问题讨论】:

【参考方案1】:

如django documentation of ForeignKey 中所述,django 只是模拟这种行为,而不是将其推迟到数据库中。

Django 模拟 SQL 约束 ON DELETE CASCADE 的行为,并删除包含 ForeignKey 的对象。

所以回答您的问题:这是预期的行为,并且仍然会像您期望它在数据库级别上一样工作。

我怎样才能让我的 models.py 文件输出级联删除外键约束?

你不能因为 django 目前不支持这个功能。不过有一张票正在讨论添加它:https://code.djangoproject.com/ticket/21961


编辑以进一步说明如何在数据库级别执行此操作

虽然我强烈建议让 django 为您处理这件事,但可能有理由不这样做。

要退出数据库表创建或删除操作,您可以在ArticleStatMeta 类中将Options.managed 设置为False。然而,这也意味着您现在有责任手动进行迁移,例如编写CREATE TABLE 语句来定义包含外键约束的表(因此您现在可以完全控制它)。要考虑的另一个考虑因素是,您应该指示 django 在删除引用的 Article 对象时不再执行任何操作(因为您的数据库现在负责该操作)。这可以通过将on_delete 设置为models.DO_NOTHING 来确保。

将您的ArticleStat 放在一起现在看起来像这样:

class ArticleStat(models.Model):
    article = models.ForeignKey(Article, on_delete=models.DO_NOTHING)

    class Meta:
        managed = False

关于信号的评论促使我重新审视这一点并列出一些需要考虑的陷阱。

选择退出也意味着退出 django 信号。特别是 pre_deletepost_delete 将不再为级联对象触发。

正如ticket description 中提到的,混合数据库级联和 django 级联不能很好地结合在一起。

如果模型 A 使用 CASCADE_DB 引用模型 B,但模型 B 使用常规 CASCADE 引用模型 C,删除 A 不会 一直级联到C。

话虽如此,我找不到任何明确的证据来证明 django 以目前的方式处理这个问题。

【讨论】:

我希望使用级联删除外键约束生成我的数据库,这现在不会发生。我需要进行哪些更改才能实现这一点,或者 Django 不支持我正在寻找的行为? Django 目前不提供对数据库级级联选项的支持。但是有一张票正在讨论添加此选项:code.djangoproject.com/ticket/21961 好的,感谢您的澄清。所以如果我想要这个,我只需要创建独立于我的模型的迁移? 我添加了一些关于如何选择退出级联删除的“django 方式”并在数据库级别强制执行的更多信息。请注意,我尚未完全测试此方法,因此您可能想先在小型测试环境中进行试验。 我要补充一点,Django 在应用程序中实现级联删除的原因是为了支持模型删除信号(pre_deletepost_delete)。如果您使用与DO_NOTHING 配对的数据库级级联删除,则不会触发这些信号。

以上是关于为啥我的模型的“on_delete=models.CASCADE”不生成级联外键约束?的主要内容,如果未能解决你的问题,请参考以下文章

Django 模型外键 on_delete=null 但外键还是被删除了

django数据模型on_delete, db_constraint的使用

尝试在 Django 中迁移模型时出现值错误

如何以 django 模型形式覆盖保存方法

Django 发票模型表单集

如果模型中的字段为空,则在保存时删除模型实例