Python Django迁移抛出1072键列';汽车制造商id&x27;不';不存在于表中
下面是简化的任务和设置(Django 1.8、MySQL、Python 2.7),我有:Python Django迁移抛出1072键列';汽车制造商id&x27;不';不存在于表中,python,mysql,django,django-migrations,Python,Mysql,Django,Django Migrations,下面是简化的任务和设置(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) cl
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模型,并更新Bike中的外键关系
# -*- 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'
)
]
我已经创建了以下迁移,它使用BikeMake中的名称更新CarMake,添加临时字段Bike.car\u make,将数据从Bike.make迁移到Bike.car\u make,删除Bike.make字段,并将Bike.car\u 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
。现在有趣的是,从DBPOV开始,一切都已完成,数据已迁移,列已重命名,只有迁移未标记为已完成,并引发错误
另外,如果我只是将migrations.RenameField
移动到一个单独的迁移文件中,并连续运行两次迁移,则一切正常,不会引发#1072错误
此外,我尝试在迁移之前插入一个断点。重命名字段
,并验证了Bike.car\u make列存在,并且我可以在该点正常获取Bike模型的所有对象
导致错误的MySQL查询如下:
CREATE INDEX `my_app_bike_c2036163` ON `my_app_bike` (`car_make_id`)
有没有办法修复它并将其放在一个迁移文件中?提前谢谢
更新04.02.16
正如@kvikshaug所指出的,之所以会出现这种情况,是因为Django在执行所有操作后创建了索引和约束,即在生成用于创建索引和/或约束的原始SQL时,会执行相应的操作(在我的例子中是AddField
),但该查询实际上是在最后运行的,因此会出现错误
对于相对较小的模式,一个可能的解决方案是使用Django并自己键入原始查询,但这相当麻烦,而且您必须自己创建约束
因此,我开始分离重命名迁移。Django迁移在执行所有操作后创建索引。第二个操作是添加字段
car\u make
,使Django添加CREATE INDEX
命令,您注意到该命令会导致错误:
CREATE INDEX `my_app_bike_c2036163` ON `my_app_bike` (`car_make_id`)
即使您后来重命名了该字段,Django仍然尝试为现在缺少的car\u make
字段创建索引,这就是为什么会出现错误。运行sqlmigrate
可以清楚地看到这一点:
$ ./manage.py sqlmigrate my_app 0002_blah_blah
BEGIN;
--
-- MIGRATION NOW PERFORMS OPERATION THAT CANNOT BE WRITTEN AS SQL:
-- Raw Python operation
--
--
-- Add field car_make to bike
--
ALTER TABLE "my_app_bike" ADD COLUMN "car_make_id" integer DEFAULT 1 NOT NULL;
ALTER TABLE "my_app_bike" ALTER COLUMN "car_make_id" DROP DEFAULT;
--
-- MIGRATION NOW PERFORMS OPERATION THAT CANNOT BE WRITTEN AS SQL:
-- Raw Python operation
--
--
-- MIGRATION NOW PERFORMS OPERATION THAT CANNOT BE WRITTEN AS SQL:
-- Raw Python operation
--
--
-- Remove field make from bike
--
ALTER TABLE "my_app_bike" DROP CONSTRAINT "my_app_bike_make_id_5615ed11_fk_my_app_bikemake_id";
ALTER TABLE "my_app_bike" DROP COLUMN "make_id" CASCADE;
--
-- Rename field car_make on bike to make
--
ALTER TABLE "my_app_bike" RENAME COLUMN "car_make_id" TO "make_id";
CREATE INDEX "my_app_bike_78e8ca60" ON "my_app_bike" ("car_make_id");
ALTER TABLE "my_app_bike" ADD CONSTRAINT "my_app_bike_car_make_id_6c42be09_fk_my_app_carmake_id" FOREIGN KEY ("car_make_id") REFERENCES "my_app_carmake" ("id") DEFERRABLE INITIALLY DEFERRED;
COMMIT;
您可以尝试将此报告为bug(或搜索;可能已经报告),但您最好按照Alasdair的建议,将迁移分开。我认为您可能试图在一次迁移中做太多的工作。我认为您可以进行多次迁移,例如1。创建新字段
car\u make
。2.填充car\u make
字段。3.删除旧的make
字段4。将car\u make
字段重命名为make
。可能是这样,但即使我最终将这些操作分离为几个迁移,我仍然想知道,为什么会出现错误。