Django - Change a ForeignKey relation to OneToOne
I am using South with my Django app. I have two models that I am changing from having a ForeignKey
relation to having a OneToOneField
relation. When I ran this migration on my dev database, it ran fine. When the migrations get ran as part of creating a test database, the latest migration fails with a MySQL 1005 error: "Can't create table mydb.#sql-3249_1d (errno: 121)". Doing some Googling revealed that this is usually a problem with trying to add a constraint with the same name as an existing constraint. The specific line in the migration that it fails on is:
The relation was changed from:
class MyModel(models.Model):
othermodel = models.ForeignKey(OtherModel)
to
class MyModel(models.Model):开发者_开发百科
othermodel = models.OneToOneField(OtherModel)
which generated the following statements in the migration:
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'])
But instead of failing on the create_unique
call, it is failing on the alter_column
call. I ran the following command to see what SQL was being generated:
python manage.py migrate myapp 0010 --db-dry-run --verbosity=2
and it printed out
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`) []
It seems strange that it is trying to run the ADD CONSTRAINT
twice, but if I remove the db.create_unique
call, no SQL is generated when I run it with --db-dry-run
, but I still get the error if I run it for real.
I am at a loss here, any help is appreciated.
You actually don't need a migration at all. OneToOne and ForeignKey relations have a compatible database schema under the hook: a simple column witht the other object ID in one of the table.
Just fake the migration with migrate --fake
if you don't want to enter in the trouble of telling south to ignore this change.
First thing I would try would be to add a db.delete_unique(...)
in various places to see if I could hack it.
Failing that, I'd split it up into 3 migrations:
- a schema migration to add a new column for the OneToOne
- a data migration to copy all FK values from old column over to new
- a schema migration to remove the old column, which then I'd edit manually to include a command to rename the new column to the same as the old one.
I agree with @e-satis that the goal here is to fake a migration, but I suggest a different approach if you're working with a team.
If you create a migration then --fake
it, all your team members will need to remember to --fake
it as well. If any of them don't do this when they upgrade, you're in trouble.
A better approach is to create an empty migration, then migrate it:
manage.py schemamigration yourapp --empty fake_migration_of_foreign_key_to_onetoone
manage.py migrate # Like you always do!
精彩评论