Вот упрощенная задача и настройка (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, поэтому я обновлю модель CarMake со значениями из BikeMake, а также обновлю отношение внешнего ключа в < strong>Велосипед.
Я создал следующую миграцию, которая обновляет CarMake именами из BikeMake, добавляет временное поле Bike.car_make, переносит данные из >Bike.make в Bike.car_make, удаляет поле Bike.make и переименовывает Bike.car_make в 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'
)
]
И когда я пытаюсь запустить его, я получаю ошибку # 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
), но этот запрос на самом деле запускается в самом конце, отсюда и ошибка.
Одним из возможных решений для относительно небольших схем может быть использование RunSQL
и сами вводите необработанные запросы, но это довольно громоздко + вам нужно создавать ограничения самостоятельно.
Поэтому я пошел с разделением миграции переименования.
car_make
. 2. Заполните полеcar_make
. 3. Удалите старое полеmake
. 4. Переименуйте полеcar_make
вmake
. - person Alasdair   schedule 02.02.2016