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 0121
first- перевести скомканную миграцию в нормальную миграцию (doc), то есть
- удалить атрибут
replace
- обновить атрибут
dependencies
в других файлах миграции, чтобы не использовать оригинальные несквошированные файлы миграции (0107 - 0121) - удалите замененные оригинальные миграции
- удалить атрибут
squashmigrations data 0104 0107_squash_0121
снова
Зависит от вашей мотивации, вся эта ручная работа с файлами миграции может не стоить того, как отметили другие комментаторы
При подавлении миграции есть и другие проблемы, которые могут быть неочевидны. Я написал заметку здесь