Django`squashmigrations` оставляет много RemoveField
Я пытаюсь использовать Django manage.py squashmigrations для уменьшения количества файлов миграции и операций миграции, после нескольких лет их накопления.
Django должен, по идее, оптимизировать миграции, и в некоторой степени он это делает.
Однако другие случаи, которые должны быть очевидно оптимизированы, пропускаются, например, ниже, где простое поле AddField+RemoveField не оптимизируется. Только AddField вставляется в CreateModel, но RemoveField все равно остается, вместо того чтобы полностью убрать поле из CreateModel.
В идеале после сквоша не должно остаться ни одного RemoveField, если я не ошибаюсь.
Миграция 1: Добавить модель
class Migration(migrations.Migration):
dependencies = [("data", "0104")]
operations = [
migrations.CreateModel(
name="MyModel",
fields=[... ]
)
]
Миграция 2: Добавление поля в модель
class Migration(migrations.Migration):
dependencies = [("data", "0107")]
operations = [
migrations.AddField(
model_name="mymodel",
name="process_name",
field=models.CharField(default=None, max_length=300, null=True),
),
]
Миграция 3: Удаление того же поля из модели
class Migration(migrations.Migration):
dependencies = [("data", "0121")]
operations = [migrations.RemoveField(model_name="mymodel", name="process_name")]
Результирующая сминаемая миграция:
class Migration(migrations.Migration):
replaces = [...]
initial = True
operations = [
migrations.CreateModel(
name="MyModel",
fields=[
(
"process_name", # Should be possible to leave out ???
models.CharField(default=None, max_length=300, null=True),
),
],
),
migrations.RemoveField(model_name="mymodel", name="process_name"), # ???
]
Ожидается ли это или что может вызвать это?
Я не использую никаких расширенных функций миграции, таких как RunSql или RunPython. Нет других таблиц или миграций, которые зависят от поля process_name.
Есть ли способ дать подсказку squashmigrations? Могу ли я запустить сквош еще раз, чтобы оптимизировать дальше?
(Django версии 5.1)
Я предполагаю, что это связано с тем, как их оптимизатор реализован .
Ваша миграция состоит из трех операций
- CreateModel
- AddField
- RemoveField
Когда запускается оптимизатор
for i, operation in enumerate(operations): right = True # Should we reduce on the right or on the left. # Compare it to each operation after it for j, other in enumerate(operations[i + 1 :]): result = operation.reduce(other, app_label) ....
Итерация начинается с i=0, operation=CreateModel и ищет все, что можно .reduce
j=0, other=AddField является сводимой, поэтому она была сведена к одной операции
if isinstance(operation, AddField): return [ CreateModel( self.name, fields=self.fields + [(operation.name, operation.field)], options=self.options, bases=self.bases, managers=self.managers, ), ]
Во время итерации i=0, j=1, other=RemoveField не является сводимым, так как поле process_name не существовало в операции i=0, operation=CreateModel.
Во время второй итерации i=1, operation=AddField выглядит оптимизируемым с RemoveField, но я полагаю, что оптимизируемая операция не была выбрана оптимизатором
if isinstance(result, list): in_between = operations[i + 1 : i + j + 1] if right: new_operations.extend(in_between) new_operations.extend(result) elif all(op.reduce(other, app_label) is True for op in in_between): # Perform a left reduction if all of the in-between # operations can optimize through other. new_operations.extend(result) new_operations.extend(in_between) else: # Otherwise keep trying. new_operations.append(operation) break new_operations.extend(operations[i + j + 2 :]) return new_operations
Я лишь бегло просмотрел исходный код, поэтому вполне вероятно, что я интерпретирую все неправильно.
Если ваша история миграции проста и все машины (production/dev/etc) находятся под вашим контролем. Возможно, будет достаточно
- сначала измените миграцию на ту, которая была до сминания
migrate data 0103.
- удалите миграции, которые вы хотите смять (с 0107 по 0121), и создайте новый набор, снова выполнив
makemigrations data. - нормально мигрировать
или с небольшим количеством ручной работы
squashmigrations data 0107 0121first- перевести скомканную миграцию в нормальную миграцию (doc), то есть
- удалить атрибут
replace - обновить атрибут
dependenciesв других файлах миграции, чтобы не использовать оригинальные несквошированные файлы миграции (0107 - 0121) - удалите замененные оригинальные миграции
- удалить атрибут
squashmigrations data 0104 0107_squash_0121снова
Зависит от вашей мотивации, вся эта ручная работа с файлами миграции может не стоить того, как отметили другие комментаторы
При подавлении миграции есть и другие проблемы, которые могут быть неочевидны. Я написал заметку здесь