Django 1.9迁移中删除外键

15

我有一个 Django 模型,其中包含对另一个模型的外键引用:

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

我希望将底层数据库列保留为字段,但是在数据库中取消外键约束。

因此,模型将更改为:

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

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

我不想删除该列并重新创建它(这是当我根据上述更改自动生成迁移时 Django 做的事情)。

我想保留该字段,但我想使用迁移从数据库中删除外键约束。我不清楚如何在 Django 迁移中执行此操作 - 是否有某种内置支持或者我需要运行一些原始 SQL,并且如果是这样做,我如何以编程方式获取约束的名称?

3个回答

22
这是我如何做到的,基于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
        )
    ]

我也尝试过,并且成功了。问题是,当我需要在此之后进行新的迁移时,它将无法识别状态更改,并且仅基于数据库模式工作。你也遇到过这个问题吗? - Jonatas CD

8
请查看SeparateDatabaseAndState。它允许您将Django(状态)迁移的部分与数据库迁移的部分分开指定。
  1. Amend the field in your models file.
  2. Create the migration, as normal. You will end up with something like:

    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)),
            ),
        ]
    
  3. Now manually amend this to:

    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关系将被修改,但数据库数据不会改变。
不用说,尝试这个之前请备份。

您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - J. Doe
你想让Django仍然认为它是一个外键,但是数据库认为它是一个普通文本字段吗? - nimasmi
抱歉,我错过了这一部分:我想保留底层的DB列(something_id),模型将更新为将something_id作为整数字段,但我想删除数据库外键约束。我会更新问题。 - J. Doe
好的,我明白了,无论如何我建议的也不会删除这个约束条件。对此我很抱歉。那么使用SeparateDatabaseAndState怎么样呢?并添加第二个操作来调用migrations.RunSQL(https://docs.djangoproject.com/en/1.9/ref/migration-operations/#runsql)如何?恐怕我不懂SQL,所以我只能提供框架而已。我意识到我还没有完全回答你的问题。 - nimasmi

5

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


1
尽管它应该这样做,但对我来说并没有。请参见https://code.djangoproject.com/ticket/30741#ticket。 - Scott Stafford
奇怪。你看过 sqlmigrate 生成的内容了吗?一个解决方法是用 RunSQL 包装迁移,并将现有操作放在 state_operations 关键字参数中,这样你就可以精确控制 db_constraint=False 的解释方式。 - btown
sqlmigrate 显示 noop,我在 Django 的那个链接的 bug ticket 中写了出来。 - Scott Stafford

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接