Django 1.9 在迁移中删除外键

Posted

技术标签:

【中文标题】Django 1.9 在迁移中删除外键【英文标题】:Django 1.9 drop foreign key in migration 【发布时间】:2016-11-13 16:04:33 【问题描述】:

我有一个 Django 模型,它具有另一个模型的外键:

class Example(models.Model)
   something = models.ForeignKey(SomeModel, db_index=True)

我想将底层的DB列保留为字段,但要摆脱数据库中的外键约束。

所以模型会变成:

class Example(models.Model):
   something_id = models.IntegerField() 

而且,需要明确的是,something_id 是 Django 为外键字段创建的列。

我不想删除该列并重新创建它(这是 Django 在如上所述更改模型后自动生成迁移时所做的)。

我想保留字段但是我想通过迁移移除数据库中的外键约束。我不清楚如何使用 Django 迁移来执行此操作 - 是否有一些内置支持,或者我是否必须运行一些原始 SQL,如果是,我如何以编程方式获取约束的名称?

【问题讨论】:

【参考方案1】:

见SeparateDatabaseAndState。它允许您从数据库中单独指定迁移的 Django(状态)部分 迁移的一部分。

    修改模型文件中的字段。

    像往常一样创建迁移。你最终会得到类似的东西:

    class Migration(migrations.Migration):
    
        dependencies = [
            ('my_app', '0001_whatever.py'),
        ]
    
        operations = [
            migrations.AlterField(
                model_name='example',
                name='something',
                field=models.CharField(max_length=255, null=True)),
            ),
        ]
    

    现在手动修改为:

    class Migration(migrations.Migration):
    
        dependencies = [
            ('my_app', '0001_whatever.py'),
        ]
    
        state_operations = [
            migrations.AlterField(
                model_name='example',
                name='something',
                field=models.CharField(max_length=255, null=True)),
            ),
        ]
        operations = [
            migrations.SeparateDatabaseAndState(state_operations=state_operations)
        ]
    

请注意,您没有指定任何 database_operations 参数,因此修改了 Django 关系,但数据库数据未更改。

不用说:在尝试之前先备份。

【讨论】:

感谢您的回答,但这不是我想要的;我想删除数据库外键约束(即我想要一个类似ALTER TABLE examples DROP CONSTRAINT x 的迁移) 你想让 Django 认为它仍然是一个外键,但数据库认为它是一个纯文本字段? 对不起,我错过了这部分:我想保留底层数据库列(something_id),模型将更新为将something_id作为整数字段,但我想删除数据库外键约束。我会更新问题 好吧,我知道我的建议不会放弃约束。对此表示歉意。使用SeparateDatabaseAndState 并添加第二个调用migrations.RunSQL (docs.djangoproject.com/en/1.9/ref/migration-operations/#runsql) 的操作怎么样?恐怕我不会说 SQL,所以这只是框架。我意识到我还没有完全回答你的问题。【参考方案2】:

这就是我设法做到的,它基于上面 nimasmi 的回答:

class Migration(migrations.Migration):
    dependencies = [
        ('my_app', '0001_initial'),
    ]

    # These *WILL* impact the database!
    database_operations = [
        migrations.AlterField(
            model_name='Example',
            name='something',
            field=models.ForeignKey('Something', db_constraint=False, db_index=True, null=False)
        ),
    ]

    # These *WON'T* impact the database, they update Django state *ONLY*!
    state_operations = [
        migrations.AlterField(
            model_name='Example',
            name='something',
            field=models.IntegerField(db_index=True, null=False)
        ),
        migrations.RenameField(
            model_name='Example',
            old_name='something',
            new_name='something_id'
        ),
    ]

    operations = [
        migrations.SeparateDatabaseAndState(
            database_operations=database_operations,
            state_operations=state_operations
        )
    ]

【讨论】:

我也试过了,而且奏效了。问题是当我需要在此之后进行新的迁移时,它不会识别状态更改并且只能基于数据库模式工作。你也穿越了吗?【参考方案3】:

从 Django 2.0 开始,将您的字段更改为 models.ForeignKey(db_constraint=False, db_index=False, ...) 将生成执行 ALTER TABLE DROP CONSTRAINT 和 DROP INDEX IF EXISTS 的迁移,这似乎正是您想要的。

【讨论】:

它并没有为我这样做,虽然它应该......见code.djangoproject.com/ticket/30741#ticket 很奇怪。你看过sqlmigrate 生成了什么吗?一种解决方法是使用 RunSQL 包装迁移并将现有操作放在 state_operations kwarg 中,这样您就可以准确控制 db_constraint=False 的解释方式。 sqlmigrate 显示了一个 noop,这是我在链接的 Django bug 票中写的。

以上是关于Django 1.9 在迁移中删除外键的主要内容,如果未能解决你的问题,请参考以下文章

迁移应用程序中缺少 django 1.9 models_module

Django 1.9 迁移问题

Python Django 1.9 迁移错误“创建新内容类型时出错...”

尝试在 Django 1.9 中迁移——奇怪的 SQL 错误 "django.db.utils.OperationalError: near ")": syntax er

使用 django-import-export 在 django 迁移中的外键

修改 Django 迁移文件,或使用 --fake 标志?