为什么我的模型的“on_delete=models.CASCADE”没有生成级联外键约束?

问题描述 投票:0回答:2

我正在使用 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 文件输出级联删除外键约束?

python django python-3.x postgresql cascading-deletes
2个回答
10
投票

ForeignKey

django 文档中所述,django 只是模拟此行为,而不是将其推迟到数据库。

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

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

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

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


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

虽然我强烈建议让 django 为你处理这个问题,但可能有理由不这样做。

要选择退出数据库表创建或删除操作,您可以将 False

Meta
类中的
Options.management
设置为
ArticleStat
。然而,这也意味着您现在负责手动进行迁移,例如编写
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_delete
    post_delete
    将不再因级联对象而被触发。

  • 票证描述中所述混合数据库和django级联不会很好地协同工作。

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

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


0
投票

使用

db_constraint=False
并在迁移中添加带有级联删除的外键约束。

例如。

class ArticleStat(models.Model):
    article = models.ForeignKey(
        Article, 
        on_delete=models.CASCADE, 
        db_constraint=False  # <----
    )
class Migration(migrations.Migration):

    ...

    operations = [
        migrations.CreateModel(
            name="ArticleStat",
            fields=[
                (
                    "article",
                    models.ForeignKey(
                        blank=True,
                        db_constraint=False,
                        null=True,
                        on_delete=django.db.models.deletion.CASCADE,
                        to="app.article",
                    ),
                ),
            ],
            options={
                "abstract": False,
            },
        ),


        # Add foreign key constraint with cascade delete.

        migrations.RunSQL(
            sql=[
                "ALTER TABLE app_articlestat ADD CONSTRAINT app_articlestat FOREIGN KEY (article_id) REFERENCES app_article (id) ON DELETE CASCADE;",
            ],
            reverse_sql=[
                "ALTER TABLE app_articlestat DROP CONSTRAINT app_articlestat_article_id;",

            ]
        )
    ]

© www.soinside.com 2019 - 2024. All rights reserved.