【发布时间】:2016-05-11 13:21:51
【问题描述】:
这是简化的任务和设置(Django 1.8、MySQL、Python 2.7),我有:
class Car(models.Model):
make = models.ForeignKey(CarMake)
class Bike(models.Model):
make = models.ForeignKey(BikeMake)
class CarMake(models.Model):
name = models.CharField(max_length=32)
class BikeMake(models.Model):
name = models.CharField(max_length=32)
现在,我需要完全放弃 BikeMake 模型,因此我使用 BikeMake 中的值更新 CarMake 模型并更新外键自行车中的关系。
我创建了以下迁移,它使用 BikeMake 中的名称更新 CarMake,添加临时字段 Bike.car_make,迁移数据从 Bike.make 到 Bike.car_make,删除 Bike.make 字段并将 Bike.car_make 重命名为 Bike.make。
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
def update_car_makes(apps, schema_editor):
"""Update CarMakes with BikeMakes"""
BikeMake = apps.get_model('my_app', 'BikeMake')
CarMake = apps.get_model('my_app', 'CarMake')
for item in BikeMake.objects.all():
if not CarMake.objects.filter(name=item.name).exists():
CarMake.objects.create(name=item.name)
def remove_car_makers(apps, schema_editor):
"""Restore original CarMake (exclude BikeMake)"""
pass
def migrate_to_car_make(apps, schema_editor):
"""Set Bike.car_make according to Bike.make"""
CarMake = apps.get_model('my_app', 'CarMake')
Bike = apps.get_model('my_app', 'Bike')
for item in Bike.objects.all():
old_make = item.make
new_make = CarMake.objects.get(name=old_make.name)
item.car_make = new_make
item.save()
def reverse_migrate_to_car_make(apps, schema_editor):
pass
def dummy_forwards(apps, schema_editor):
# Empty forward migration needed for having custom backwards migration
pass
def restore_make_column_data(apps, schema_editor):
BikeMake = apps.get_model('products', 'BikeMake')
Bike = apps.get_model('products', 'Bike')
for item in Bike.objects.all():
old_make = item.bike_make
new_make = BikeMake.objects.get(name=old_make.name)
item.make = new_make
item.save()
class Migration(migrations.Migration):
dependencies = [('my_app', '0001_blah_blah')]
operations = [
migrations.RunPython(
update_car_makers,
reverse_code=remove_car_makers
),
migrations.AddField(
model_name='bike',
name='car_make',
field=models.ForeignKey(default=1, to='my_app.CarMake'),
preserve_default=False
),
migrations.RunPython(
migrate_to_car_make,
reverse_code=reverse_migrate_to_car_make
),
migrations.RunPython(
dummy_forwards,
reverse_code=restore_make_column_data
),
migrations.RemoveField(
model_name='bike',
name='make',
),
migrations.RenameField(
model_name='bike',
old_name='car_make',
new_name='make'
)
]
当我尝试运行它时,在运行最后一个操作时出现 #1072 错误:migrations.RenameField。现在有趣的是,从 DB POV 开始,一切都已完成,数据已迁移,列重命名,只有迁移未标记为已完成并引发错误。
另外,如果我只是将 migrations.RenameField 移动到单独的迁移文件并连续运行两个迁移 - 一切正常,并且不会引发 #1072 错误。
此外,我尝试在 migrations.RenameField 之前插入断点,并验证 Bike.car_make 列存在并且我可以正常获取 Bike的所有对象> 那时的模型。
导致错误的 MySQL 查询如下:
CREATE INDEX `my_app_bike_c2036163` ON `my_app_bike` (`car_make_id`)
任何想法如何修复它并将其包含在一个迁移文件中?提前致谢!
更新 04.02.16
正如@kvikshaug 指出的那样,这是因为Django 在执行所有操作后创建索引和约束,即当时生成用于创建索引和/或约束的原始SQL,执行相应的操作(在我的情况下为AddField) ,但该查询实际上是在最后运行,因此出现错误。
对于相对较小的模式,一种可能的解决方案是使用 Django 的 RunSQL 并自己键入原始查询,但这非常麻烦 + 您必须自己创建约束。
所以我开始分离重命名迁移。
【问题讨论】:
-
我认为您可能试图在一次迁移中做太多工作。我认为你可能会更好地进行几次迁移,例如1. 创建新字段
car_make。 2. 填充car_make字段。 3. 删除旧的make字段 4. 将car_make字段重命名为make。 -
可能是这样,但即使我最终将这些操作分成几个迁移,我仍然想知道,为什么会出现错误。
标签: python mysql django django-migrations