Модуль 13

Міграції бази даних

Ось урок на тему "Міграції бази даних", створений у стилі CS50: енергійний, зрозумілий і орієнтований на "чому", а не просто "як".


🎓 CS50: Міграції бази даних (Database Migrations)

Вітаю, друзі! 👋

Уявіть, що ми з вами будуємо хмарочос. Ми залили фундамент, звели стіни, провели електрику. Люди вже заселилися, живуть, варять каву, дивляться серіали.

І тут замовник каже: "Слухай, а давай перенесемо ліфт на іншу стіну і додамо басейн на 5-му поверсі?"

У будівництві це катастрофа. Вам доведеться виселяти людей і ламати стіни. Але в програмуванні ми робимо це щодня! 🏗️

Як змінити структуру бази даних, коли в ній вже є дані живих користувачів, не видаляючи все під нуль? Тут на сцену виходять Міграції.


1. 🔥 Вступ: Проблема та мотивація

Давайте чесно: коли ви тільки вчитеся, ви часто робите так: 1. Створили таблицю Users. 2. Зрозуміли, що забули додати поле email. 3. DROP TABLE Users (видалили таблицю). 4. CREATE TABLE Users (створили нову з email).

Це працює, поки ви один. Але уявіть, що це Facebook. У вас мільярд користувачів. Ви ж не скажете Цукербергу: "Марк, мені треба видалити базу на хвилинку, нехай усі зареєструються заново, бо я забув додати колонку для TikTok-посилань"? 😅

Питання до вас: Як нам змінити креслення будинку, не руйнуючи сам будинок?

Нам потрібен спосіб еволюціонувати схему бази даних крок за кроком. Нам потрібна історія змін. Нам потрібна "машина часу". Це і є міграції.

Без них будь-яка зміна на "живому" проєкті (production) перетворюється на рулетку: пощастить — дані збережуться, не пощастить — шукайте нову роботу.


2. 🧠 Теоретична база (без нудних лекцій)

Давайте заглянемо під капот. Що таке міграція фізично?

Це просто файл з інструкціями. Зазвичай він має дві частини (назвемо їх методами):

  1. ⬆️ UP (Вгору): Що треба зробити? (Наприклад: "Додай колонку age").
  2. ⬇️ DOWN (Вниз): Як це скасувати, якщо ми помилилися? (Наприклад: "Видали колонку age").

Це як Git, але для структури бази даних.

Як це працює?

Уявіть, що ваша база даних веде щоденник. У ній є спеціальна службова таблиця (часто називається migrations або alembic_version). Коли ви запускаєте команду "оновити базу", програма дивиться в цей щоденник: * "Ага, міграцію №1 ми вже робили." * "Міграцію №2 теж." * "О! З'явилася міграція №3, якої в мене немає. Виконую її!"

🔑 Що треба запам’ятати залізно:

  • Міграції гарантують консистентність. У всіх розробників у команді буде однакова структура БД.
  • Міграції мають бути зворотними. Завжди пишіть DOWN, щоб мати шлях для відступу.

3. 🧪 Приклади: Від "Hello World" до реальності

Давайте подивимось, як це виглядає. Я буду використовувати псевдокод, схожий на SQL та Python, щоб ви зрозуміли суть, незалежно від мови.

Приклад 1: Початок (Initial Migration)

Ми створюємо таблицю користувачів.

Файл: 001_create_users_table

def up():
    CREATE TABLE users (
        id INTEGER PRIMARY KEY,
        username VARCHAR(50) NOT NULL
    );

def down():
    DROP TABLE users;

Тут все просто. Ми створили світ. Якщо треба відкотити — знищуємо світ.


Приклад 2: Реальна потреба

Проєкт живе місяць. Маркетологи кажуть: "Нам треба знати, чи підписаний користувач на розсилку!". Треба додати колонку is_subscribed.

Питання до вас: Що буде, якщо ми просто додамо цю колонку? Яке значення буде у тих 1000 користувачів, які ВЖЕ є в базі? 🤔 (Пауза на подумати) Правильно, NULL (пустота). А якщо нам не можна NULL? Нам треба значення за замовчуванням (DEFAULT).

Файл: 002_add_is_subscribed

def up():
    # Додаємо колонку, і для старих записів ставимо FALSE
    ALTER TABLE users 
    ADD COLUMN is_subscribed BOOLEAN DEFAULT FALSE;

def down():
    # Прибираємо сліди нашого експерименту
    ALTER TABLE users 
    DROP COLUMN is_subscribed;

Бачите? Ми не чіпали існуючі дані (username і id). Ми просто "прибудували балкон".


Приклад 3: Складний випадок (Data Migration)

Це рівень сеньйора. У нас була колонка full_name ("Taras Shevchenko"), а тепер ми хочемо розділити її на first_name і last_name.

Тут мало змінити структуру (схему), треба змінити дані.

Файл: 003_split_names

def up():
    # 1. Створюємо нові колонки
    ALTER TABLE users ADD COLUMN first_name VARCHAR;
    ALTER TABLE users ADD COLUMN last_name VARCHAR;

    # 2. Магія: переносимо дані (скрипт)
    FOR user IN users:
        parts = user.full_name.split(" ")
        user.first_name = parts[0]
        user.last_name = parts[1]
        SAVE user;

    # 3. Видаляємо стару колонку
    ALTER TABLE users DROP COLUMN full_name;

def down():
    # О ні! Треба повернути все назад
    ALTER TABLE users ADD COLUMN full_name VARCHAR;

    FOR user IN users:
        user.full_name = user.first_name + " " + user.last_name
        SAVE user;

    ALTER TABLE users DROP COLUMN first_name;
    ALTER TABLE users DROP COLUMN last_name;

4. 🛠 Практична частина

Час забруднити руки! Уявіть, що ви працюєте над інтернет-магазином.

Завдання 1: Ініціалізація Напишіть (на папері або в редакторі) міграцію UP для створення таблиці Products (поля: id, name, price). Не забудьте DOWN.

Завдання 2: Забудькуватість Ви забули додати поле "Кількість на складі" (stock_quantity). Напишіть нову міграцію, щоб додати це поле.

Завдання 3: Критичне мислення У вас є таблиця Orders. Ви хочете змінити тип колонки order_id з тексту на число. * Питання: Що станеться, якщо в базі вже є замовлення з id "ORD-555"? Чи пройде міграція успішно? Як це вирішити?

Завдання 4: Міні-кейс Прийшов менеджер і каже: "Ми видаляємо функцію 'Відгуки' з сайту, вона нікому не треба". Ви пишете міграцію DROP TABLE reviews. Через тиждень менеджер біжить у сльозах: "Поверни все назад, клієнти скаржаться!". * Питання: Чи допоможе тут метод DOWN? Чому? (Підказка: куди діваються дані при DROP TABLE?)


5. 💡 Мислення як у розробника

Ось що відрізняє новачка від профі в темі міграцій:

⚠️ Головна заповідь: Не змінюйте минуле!

Якщо ви вже "закомітили" (зберегли в Git) файл міграції і ваші колеги його застосували — ніколи не редагуйте цей файл. Якщо ви зробили помилку в міграції №5, не виправляйте файл №5. Створіть міграцію №6, яка виправляє помилку. Чому? Бо інакше у вас і вашого колеги будуть різні версії "реальності" під одним номером. Це пекло для дебагінгу.

🧠 Як думає сеньйор:

  1. "Чи безпечно це?" Чи не заблокує ця міграція базу даних на годину, поки вона виконується? (На великих базах додавання колонки може "повісити" сайт).
  2. "Чи можу я відкотитися?" Сеньйор завжди тестує DOWN перед тим, як відправити код на сервер.
  3. "Дані — це золото". Схема — це просто скелет. Дані — це плоть. Втратити схему не страшно, втратити дані користувачів — це кінець бізнесу.

6. 🧩 Підсумок

Отже, що ми сьогодні зробили?

  1. Зрозуміли, що база даних — це живий організм, який постійно росте.
  2. Дізналися, що міграції — це спосіб керувати цим ростом безпечно, як архітектор з кресленнями.
  3. Вивчили мантру: UP будує, DOWN рятує.
  4. Зрозуміли, що історію (старі файли міграцій) переписувати не можна.

Тепер ви вмієте: Не просто створювати таблиці, а керувати часом вашої бази даних. Ви можете рухати проєкт вперед і, що важливіше, безпечно повертати його назад, якщо щось піде не так.

🔜 У наступній серії: Ми писали SQL руками. Це круто, але іноді довго. На наступному уроці ми познайомимося з ORM (Object-Relational Mapping). Уявіть, що ви пишете звичайний код на Python/Java/JS, а він сам перетворюється на SQL-міграції. Магія? Ні, технології!

А поки що — спробуйте не "дропнути" базу! 😉 Це був CS50. Побачимось!