Django - 将ForeignKey关系更改为OneToOne

19 投票
3 回答
7261 浏览
提问于 2025-04-17 01:45

我在用South工具和我的Django应用程序一起工作。我有两个模型,我正在把它们的关系从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,但如果我真的运行它,还是会出现错误。

我对此感到很困惑,任何帮助都非常感谢。

3 个回答

1

我同意@e-satis的看法,这里主要是为了模拟一个迁移过程,但如果你和团队一起工作,我建议换个方法。

如果你创建了一个迁移,然后用--fake来处理,所有团队成员都得记得也要用--fake。如果其中有人在升级时忘了这样做,那就麻烦了。

更好的方法是先创建一个空的迁移,然后再进行迁移:

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

我首先会尝试在不同的地方加一个 db.delete_unique(...),看看能不能解决问题。

如果这样不行,我会把这个过程分成三个步骤:

  1. 第一步是做一个结构迁移,添加一个新的列来处理一对一的关系。
  2. 第二步是做一个数据迁移,把旧列里的所有外键值复制到新列里。
  3. 第三步是再做一个结构迁移,删除旧列,然后我会手动编辑这个迁移,加入一个命令,把新列的名字改成和旧列一样。
14

其实你根本不需要进行迁移。OneToOne和ForeignKey关系在数据库中是兼容的,简单来说,就是在某个表里有一列存放另一个对象的ID。

如果你不想麻烦地告诉south忽略这个变化,可以用 migrate --fake 来假装进行了一次迁移。

撰写回答