Зворотньо не сумісні Джанго ОРМ міграції та синьо зелений деплой
Опубліковано

Зворотньо несумісні міграції
Деякі міграції можуть бути зворотно несумісними та призвести до переривання роботи на деякий період часу між моментами коли застосовується міграція та моментом ротації докер подів. Це також буде проблемою для синьо-зеленого розгортання, якщо ви вирішите його реалізувати. Щоб уникнути такого простою, міграцію слід виконувати в 2 кроки.
Увага
- Використовуйте цей метод лише для перелічених нижче операцій. Цей спосіб небезпечний. Він може диссинхронізувати стан програми з БД. Ніколи не використовуйте самостійно написаний SQL. Завжди копіюйте його з команди
sqlmigrate
. - Цей тип міграції важко повернути зі зрозумілих причин, тому будьте дуже обережні, виконуючи це.
- Виконання цієї операції без використання
SeparateDatabaseAndState
призведе до простою. Робочі версії відмовляться починати, якщо зміни внесено до моделі, але не застосовано до внутрішнього стану django.
Список операцій роблячих міграції зворотньо несумісними.
RemoveField
DeleteModel
AddField
, коли поле не обнуляється (в декларації поля немаєnull=True
)
Рішення
Створіть 2 PR і випустіть їх у 2-ї окремих релізах:
PR видалення використання
- Видаліть усі використання поля з коду
- Зніміть поле з моделі
- Автоматично генеруйте міграцію (
./manage.py makemigrations your_app
) - Отримайте SQL міграції (він нам знадобиться пізніше) (
./manage.py sqlmigrate your_app 0002_auto_20240328_1527
) - Перемістіть зворотньо несумісні операції до
state_operations
вSeparateDatabaseAndState
(приклад нижче).
PR змін в БД
- Створіть порожню міграцію (
./manage.py makemigrations your_app --empty
) - Знову додайте
SeparateDatabaseAndState
- Помістіть SQL, отриманого з кроку 4 попереднього PR-у, в
database_operations
. Без коментарів та транзацій.
Приклад
Автоматично згенерована міграція:
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('team_management', '0001_auto_20240319_1146'),
]
operations = [
migrations.RemoveField(
model_name='organization',
name='session_expiration_time',
),
]
PR 1
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('team_management', '00001_auto_20240319_1146'),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.RemoveField(
model_name='organization',
name='session_expiration_time',
),
]
),
]
PR 2
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('team_management', '0002_auto_20240328_1527'),
]
operations = [
migrations.SeparateDatabaseAndState(
database_operations=[
migrations.RunSQL(
sql=(
'ALTER TABLE "team_management_organization" DROP COLUMN "session_expiration_time" CASCADE;'
),
),
]
),
]