Django - 将 ForeignKey 关系更改为 OneToOne

Posted

技术标签:

【中文标题】Django - 将 ForeignKey 关系更改为 OneToOne【英文标题】:Django - Change a ForeignKey relation to OneToOne 【发布时间】:2011-11-12 14:13:10 【问题描述】:

我在我的 Django 应用中使用 South。我有两个模型,我正在从 ForeignKey 关系更改为 OneToOneField 关系。当我在我的开发数据库上运行此迁移时,它运行良好。当迁移作为创建测试数据库的一部分运行时,最新的迁移失败并出现 mysql 1005 错误:“无法创建表 mydb.#sql-3249_1d (errno: 121)”。做一些谷歌搜索表明,这通常是尝试添加与现有约束同名的约束时出现的问题。迁移失败的具体行是:

关系更改自:

class MyModel(models.Model):
    othermodel = models.ForeignKey(OtherModel)

class MyModel(models.Model):
    othermodel = models.OneToOneField(OtherModel)

在迁移中生成了以下语句:

db.alter_column('myapp_mymodel', 'othermodel_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['myapp.OtherModel'], unique=True))

db.create_unique('myapp_mymodel', ['othermodel_id'])

但不是在create_unique 调用上失败,而是在alter_column 调用上失败。我运行了以下命令来查看正在生成什么 SQL:

python manage.py migrate myapp 0010 --db-dry-run --verbosity=2

打印出来了

myapp:0010_auto__chg_field_mymodel_othermodel__add_unique_mymodel
   = ALTER TABLE `myapp_mymodel` ADD CONSTRAINT `myapp_mymodel_othermodel_id_uniq` UNIQUE (`othermodel_id`) []
   = SET FOREIGN_KEY_CHECKS=1; []
   = ALTER TABLE `myapp_mymodel` ADD CONSTRAINT `myapp_mymodel_othermodel_id_uniq` UNIQUE (`othermodel_id`) []

它尝试运行ADD CONSTRAINT 两次似乎很奇怪,但是如果我删除db.create_unique 调用,当我使用--db-dry-run 运行它时不会生成SQL,但如果我运行我仍然会收到错误真的。

我在这里不知所措,感谢任何帮助。

【问题讨论】:

几天前我创建了完全相同的迁移,并且运行良好。你能用不同的数据库后端尝试相同的代码吗(我是在 PostgreSQL 数据库上做的)。另外,请检查您的 South 版本。 希望我能提供帮助 - 我进行了更改,它生成了相同的 python 和 SQL 代码,并且迁移运行良好,使用 mysql 5.1.56 for win32。 在 South 邮件列表中提出这个问题,您很可能会找到答案。 【参考方案1】:

您实际上根本不需要迁移。 OneToOne 和 ForeignKey 关系在挂钩下具有兼容的数据库架构:一个简单的列,其中一个表中具有另一个对象 ID。

如果您不想麻烦地告诉南忽略此更改,请使用 migrate --fake 伪造迁移。

【讨论】:

如果您完全怀疑@e-satis 对此是否正确,请从代码中获取此花絮 (django.db.models.fields.related):“OneToOneField 与 ForeignKey 基本相同,除了始终带有“唯一”约束的例外”并请注意,OneToOne 实际上是从 ForeignKey 继承的,只进行了一些小的调整。 确切地说,将字段从 ForeignKey 更改为 OneToOneField 会对数据库产生影响(至少对那些支持约束),不应该跳过:ForeignKey 不是唯一的,而 OneToOneField 是.在 Django 1.8 中 - 在 mysql 后端使用本机命令 makemigrations - 我完全没有问题。迁移在数据库级别正确删除了字段上之前的非唯一索引,并设置了新的唯一索引。 与 Django 3.2 的 @AugustoDestrero 相同的评论【参考方案2】:

我会尝试的第一件事是在不同的地方添加db.delete_unique(...),看看我是否可以破解它。

如果做不到这一点,我会将其拆分为 3 个迁移:

    为 OneToOne 添加新列的架构迁移 将所有 FK 值从旧列复制到新列的数据迁移 架构迁移以删除旧列,然后我将手动编辑它以包含将新列重命名为与旧列相同的命令。

【讨论】:

这应该是正确答案,很有帮助的蓝图【参考方案3】:

我同意@e-satis 的观点,即这里的目标是伪造迁移,但如果您与团队合作,我建议采用不同的方法。

如果您创建迁移然后--fake 它,您的所有团队成员也需要记住--fake 它。如果他们中的任何一个在升级时不这样做,那么您就有麻烦了。

更好的方法是创建一个空迁移,然后迁移它:

manage.py schemamigration yourapp --empty fake_migration_of_foreign_key_to_onetoone
manage.py migrate  # Like you always do! 

【讨论】:

以上是关于Django - 将 ForeignKey 关系更改为 OneToOne的主要内容,如果未能解决你的问题,请参考以下文章

Django 外键关系应该是用户到模型还是模型到用户?

django ManyToManyField多对多关系

任何人都可以仅使用 ForeignKey 在两个 django 模型之间建立多对多关系吗?

来自 ForeignKey 的 django 反向关系查询

Django、ForeignKey 关系和 Q 或

理解 / mySQL 又名欺骗 Django 中的 ForeignKey 关系