Модуль 10 · Урок 37 із 58
PostgreSQL schema: types, keys, constraints і migrations
База даних починається не з ORM-моделі, а з контракту: які факти зберігаємо, хто ними володіє, що є ідентичністю, які стани заборонені та як змінити схему без втрати даних.
Безпечна PostgreSQL-практика модуля 10
Пакет містить synthetic schema/seed/query files, parameterized Python repository, contract tests і read-only verifier. Локальні тести не виконують PostgreSQL; integration run проводиться лише у власній disposable database без персональних чи production-даних, credentials і production endpoints.
Таблиця зберігає факти, а не екран
Relational schema моделює сутності, атрибути та зв’язки незалежно від поточного UI. Для навчального issue tracker маємо projects і tickets: project існує самостійно, ticket належить рівно одному project. Це рішення визначає foreign key, а не розташування полів у формі.
Тип — частина доменного контракту
text, integer, boolean, date, timestamp with time zone та інші типи мають різні операції й межі. Не слід зберігати дату, суму або boolean як довільний text лише тому, що Python зможе його прочитати. Тип БД відсікає некоректні стани до того, як вони розійдуться між застосунками.
| Поле | Приклад contract | Поганий shortcut |
|---|---|---|
created_at | timestamptz NOT NULL | Локальний час у text |
priority | smallint CHECK (priority BETWEEN 1 AND 5) | Будь-який integer |
status | CHECK із контрольованим набором | Неперевірений label із UI |
project_id | Foreign key до project | Назва project як зв’язок |
NULL не означає порожній рядок
NULL означає відсутність відомого значення. У SQL порівняння з NULL має three-valued logic, тому для перевірки використовують IS NULL або IS NOT NULL. Якщо поле обов’язкове за доменом, ставимо NOT NULL; якщо значення справді може бути невідомим, описуємо цей стан і не підміняємо його '', 0 чи магічною датою.
Default не ремонтує поганий model
Default корисний, коли БД має право визначити значення, наприклад created_at DEFAULT now(). Він не повинен приховувати пропущений обов’язковий input або змінювати бізнес-семантику без явного рішення.
Constraints захищають усіх клієнтів
Validation у Python дає зручне повідомлення користувачеві, а constraint у PostgreSQL забезпечує останню межу цілісності для CLI, API, import job і майбутніх застосунків. Primary key, UNIQUE, foreign key, NOT NULL і CHECK виконують різні ролі; один не замінює інший.
CREATE TABLE tickets (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
project_id bigint NOT NULL REFERENCES projects(id),
title text NOT NULL CHECK (length(trim(title)) BETWEEN 3 AND 120),
status text NOT NULL CHECK (status IN ('open', 'closed')),
created_at timestamptz NOT NULL DEFAULT now()
);Delete policy треба обрати свідомо: RESTRICT, CASCADE або SET NULL змінюють доменну поведінку, а не лише синтаксис.
Migration — версійований перехід
Schema change планують як precondition, forward step, data backfill, validation, application rollout і rollback/forward-fix strategy. Для великої таблиці небезпечно мовчки додати важкий rewrite або довге блокування. У навчальному пакеті migration працює лише в окремій локальній schema; production connection string заборонений.
- Зафіксуйте поточний schema contract і залежні queries.
- Додайте сумісний nullable column або нову structure.
- Backfill виконуйте контрольованими batches із перевіркою counts.
- Увімкніть constraint після очищення старих rows.
- Тільки потім видаліть legacy path у наступному release.
Методичні джерела
Урок, сценарії, пояснення й вправи створені SEOWORK. Посилання ведуть лише на офіційну документацію та Python Wiki.
Закріпіть матеріал уроку
Три сценарні питання. Для зарахування уроку потрібно дати щонайменше дві правильні відповіді.