Отже, ми плавно підійшли до ще складнішого, але дуже практичного питання — як синхронізувати міграції в командах розробки. Річ, що здається простою на початкових етапах, може перетворитися на справжній кошмар у великих проєктах, якщо її ігнорувати. Що ж, досить прелюдій — переходимо до справи.
Уявіть, що ваша команда розробників працює над великим проєктом, який включає безліч таблиць і моделей бази даних. Один розробник додає нове поле в таблицю users, інший змінює структуру таблиці orders, а третій взагалі вирішує додати нову таблицю products. Кожен із них створює свої міграції, працює локально, і все це виглядає досить невинно. Доки... доки ви всі не намагаєтесь злити свої зміни в основний репозиторій.
"Merge conflict in migration file" — одна з найненависніших рядків, яку можна побачити. А якщо до цього додати непорозуміння через різні версії бази даних на локальних машинах, тестових серверах і в production, то результат може бути катастрофічним.
Отже, синхронізація міграцій у команді — це не просто «гарна традиція», а реальна необхідність для збереження узгодженості проєкту.
Як зробити синхронізацію міграцій простішою?
1. Використання систем контролю версій для міграцій
Почнемо з основ. Усі міграції обов'язково мають бути у вашому репозиторії. Це очевидно, але давайте все ж зафіксуємо. Коли розробник додає нову міграцію, її потрібно закомітити й запушити в загальний репозиторій.
Як це виглядає на практиці?
Припустимо, розробник A додає нове поле в модель User і генерує міграцію:
alembic revision --autogenerate -m "Add last_login field to User"
Це створює файл міграції, наприклад versions/202310110101_add_last_login_field_to_user.py.
Потім він комітить його в Git:
git add versions/202310110101_add_last_login_field_to_user.py
git commit -m "Added migration for last_login field"
git push origin feature/last-login-field
Усі члени команди повинні витягнути ці зміни й застосувати міграцію у себе:
git pull origin main
alembic upgrade head
Так ми мінімізуємо проблеми, пов'язані з "забутими" міграціями.
2. Визначення порядку роботи з міграціями
Часто робота над міграціями йде паралельно, і це створює конфлікти. Наприклад, двоє розробників створюють міграції, не знаючи про наявність міграції один одного. В результаті Alembic бачить, що є два "батьки" для однієї й тієї ж міграції, і відмовляється їх застосовувати.
Що робити в такому випадку? Ваша команда має домовитися про наступний робочий процес:
- Перевірка перед створенням міграції. Перш ніж створювати нову ревізію, стягніть останні зміни з основного репозиторію і переконайтеся, що ваші локальні міграції в актуальному стані:
git pull origin main alembic upgrade head - Створення міграції. Лише після цього створюйте нову ревізію.
- Обов'язково тестуйте міграції! Застосуйте її локально й переконайтеся, що все працює як очікується. Ніхто не хоче бути людиною, що зламала базу даних.
- Пушення міграції в основний репозиторій. Комітьте й пуште вашу міграцію в загальний репозиторій.
Погляньмо на практиці. Припустимо, розробник B працює з таблицею Orders. Перед тим як створити свою міграцію, він виконує:
git pull origin main
alembic upgrade head
Після цього робить зміни, наприклад додає нове поле order_status, генерує міграцію і пушить її. Це вбереже нас від безлічі непотрібних конфліктів.
Робота з конфліктами міграцій
Навіть при грамотній організації процесу конфлікти міграцій неминучі, особливо якщо проєкт активно розвивається. Як бути?
Уявіть, що у вас є такі міграції:
versions/202310110101_add_last_login_field_to_user.py(розробник A)versions/202310110102_add_order_status_field_to_orders.py(розробник B)
Коли ви намагаєтесь застосувати міграції, Alembic скаржиться, що в них два різні "батьки".
Alembic дозволяє вказати, що одна міграція має бути "дитиною" двох міграцій. У цьому випадку створюється так звана "merge-міграція".
alembic revision -m "Merge migrations 101 and 102" --head <head_one> --splice
Наприклад:
alembic revision -m "Merge migrations add_last_login_field and add_order_status_field" --head 202310110101 --splice
В результаті буде створено новий файл міграції, який об'єднує обидва шляхи.
Автоматизація міграцій у CI/CD
Автоматизація — ваш найкращий друг, особливо в великих проєктах. Замість того щоб змушувати розробників вручну застосовувати міграції в кожному середовищі, налаштуйте процес так, щоб це робилося автоматично під час деплою.
Налаштування в CI/CD
На етапі розгортання додайте команду, яка автоматично застосовуватиме міграції:
alembic upgrade head
Якщо ви використовуєте GitHub Actions, крок виглядатиме приблизно так:
- name: Apply Alembic migrations
run: |
alembic upgrade head
Це гарантує, що на staging і production завжди актуальна структура бази даних.
Важливість домовленостей у команді
Технічні інструменти хороші, але нічого не працює, якщо команда не дотримується правил. Ось кілька порад:
- Регулярні синхронізації. Обговорюйте зміни в базі даних на щотижневих зустрічах.
- Документація міграцій. Описуйте кожну міграцію в комітах і коментарях.
- Не поспішайте. Давайте змінам у базі даних більше часу на обговорення, щоб уникнути поспіху і помилок.
Складні кейси і як їх вирішувати
- Проблема: різні версії бази даних на локальних машинах і серверах
Найпростіший спосіб уникнути цієї проблеми — автоматизація. Налаштуйте процес так, щоб база даних у Docker завжди містила останню версію. Наприклад:
Dockerfile
CMD alembic upgrade head && uvicorn app:app --host 0.0.0.0 --port 8000 - Проблема: пропущені міграції
Додайте перевірку в CI/CD, яка порівнює поточний стан моделей з останньою застосованою міграцією. Для цього можна написати тест з використанням Alembic:
from alembic.script import ScriptDirectory from alembic.config import Config def test_migrations(): alembic_cfg = Config("alembic.ini") script = ScriptDirectory.from_config(alembic_cfg) assert script.get_heads(), "There are unapplied migrations!"
Використовуйте ці підходи, щоб підтримувати узгодженість міграцій у вашій команді. Робота з базою даних у команді може бути безболісною, якщо слідувати найкращим практикам і трохи автоматизувати рутину. Побачимось на наступній лекції!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ