Ось готовий урок, створений за твоїм майстер-промптом. Це класичний CS50-вайб: енергійно, зрозуміло і з фокусом на тому, навіщо ми це робимо.
🎓 CS50: Валідація даних на рівні ORM
Привіт, світе! 👋 Радий бачити вас на цьому занятті.
Сьогодні ми говоримо про одну з найважливіших речей у розробці бекенду — Валідацію даних. Але не просто валідацію, а ту, що відбувається на рівні вашої ORM (Object-Relational Mapping).
1. 🔥 Вступ: Фейс-контроль вашої бази даних
Уявіть собі елітний нічний клуб. Це ваша База Даних (БД). Там зберігається найцінніше: дані користувачів, транзакції, історія замовлень.
А тепер уявіть, що у клубу немає охорони на вході. Будь-хто може зайти: людина без документів, хтось із забороненими предметами або навіть той, хто взагалі не людина, а манекен. Якщо такі "гості" потраплять усередину, почнеться хаос.
У світі коду:
* Ви очікуєте вік користувача 25, а приходить -5.
* Ви чекаєте email, а отримуєте набір емодзі 💩💩💩.
* Ви чекаєте дату народження, а отримуєте текст "вчора".
Питання до вас: Як ви думаєте, що станеться, якщо ми спробуємо записати ці нісенітниці прямісінько в базу даних?
У найкращому випадку — база даних видасть страшну помилку і ваш додаток "впаде" з 500-м кодом (Internal Server Error). У найгіршому — вона це мовчки проковтне, і через місяць ваші звіти покажуть, що середній вік ваших клієнтів — мінус три роки.
Валідація на рівні ORM — це той самий суворий охоронець (фейс-контроль), який стоїть перед входом у клуб і перевіряє паспорт кожного об'єкта до того, як пропустити його всередину (зберегти в БД).
2. 🧠 Теоретична база (Що там під капотом?)
Давайте розберемося без зайвих академізмів.
ORM (Object-Relational Mapping) — це прошарок між вашим кодом (Python, JS, C#) і базою даних (SQL). Ви працюєте з класами та об'єктами, а ORM перекладає це в таблиці та рядки.
Рівні захисту (Це важливо зрозуміти!)
Валідація буває трьох типів, як три кордони:
- Frontend (браузер): Для зручності користувача (щоб червона рамка підсвітилася миттєво). Це ненадійно, хакер це обійде за 2 секунди.
- Database (SQL Constraints): Останній рубіж. Надійно, але якщо дійде до цього, база "плюне" помилкою, яку важко читати.
- Application / ORM (наша тема): Золота середина. Тут ми пишемо логіку перевірки на зрозумілій мові програмування.
Як це працює?
Коли ви викликаєте команду save() або commit():
1. ORM дивиться на дані, які ви пхаєте в об'єкт.
2. Вона проганяє їх через список правил (валідаторів), які ви написали.
3. Якщо хоч одне правило порушено — ORM кидає виняток (Exception) і навіть не намагається турбувати базу даних. SQL-запит не відправляється.
📌 Запам'ятайте: Валідація в ORM захищає цілісність даних і дає можливість повернути користувачеві зрозумілу помилку ("Вибачте, пароль надто короткий"), а не системний краш.
3. 🧪 Приклади (Від простого до реального)
Припустимо, ми пишемо на Python (використовуючи псевдокод у стилі SQLAlchemy або Django), але логіка єдина для всіх мов.
Приклад 1: Простий (Вік)
У нас є клас User. Ми хочемо, щоб вік був повнолітнім (18+).
class User(Model):
name = String()
age = Integer()
# Валідатор
def validate_age(self, value):
if value < 18:
raise ValueError("Ой! Тобі має бути 18 років.")
return value
Питання: Якщо я створюю юзера з віком 10 і натискаю "Зберегти", чи з'явиться новий рядок у базі даних?
Ні. Код зупиниться на рядку if value < 18.
Приклад 2: Реальний (Email)
Перевірка email — класика. Ми не хочемо зберігати "abrakadabra" як пошту.
import re # бібліотека регулярних виразів
class User(Model):
email = String()
def validate_email(self, value):
if "@" not in value:
raise ValueError("Це не схоже на email. Де собачка @?")
if not re.match(r"[^@]+@[^@]+\.[^@]+", value):
raise ValueError("Некоректний формат пошти.")
return value
Чому це важливо? Тому що завтра вам треба буде відправити цьому користувачу чек про оплату. Якщо в базі сміття — бізнес втрачає гроші.
Приклад 3: Складний (Залежні поля)
Уявіть систему доставки. Якщо ви обрали "Доставка кур'єром", ви зобов'язані вказати адресу. Якщо "Самовивіз" — адреса не потрібна.
class Order(Model):
delivery_type = String() # "courier" або "pickup"
address = String()
def clean(self):
# Цей метод запускається перед збереженням всього об'єкта
if self.delivery_type == "courier" and not self.address:
raise ValidationError("Для кур'єрської доставки потрібна адреса!")
Що ви очікуєте побачити?
Якщо я створю замовлення: type="courier", address="" — система не дасть це зберегти. Це і є бізнес-логіка в дії.
4. 🛠 Практична частина
Час закачати рукави! Ось ваші завдання. Уявіть, що ви працюєте над backend-частиною Instagram.
Завдання 1: Повторити базу
Напишіть валідатор для поля username. Правило: ім'я не може бути коротшим за 3 символи.
Завдання 2: "Ні" лайці
Додайте перевірку для поля comment. Якщо коментар містить слово "дурень" (або інше погане слово), викидайте помилку "Будьте ввічливі!".
Завдання 3: Виправити помилку Джуніор написав такий код для знижки:
def validate_discount(self, value):
if value > 100:
return True # Помилка тут!
Що не так? Чому це небезпечно? (Підказка: що станеться, якщо знижка -50%?). Виправте це, щоб знижка була тільки в межах від 0 до 100.
Завдання 4: Міні-кейс (Банкінг)
У вас є модель Transaction з полями sender_balance та amount.
Напишіть валідацію, яка не дозволяє переказати суму (amount), якщо вона більша за поточний баланс (sender_balance).
Завдання 5: А що, якщо... Що, якщо ми змінимо дані прямим SQL-запитом через консоль адміністратора бази даних, оминаючи наш код? Чи спрацює наша ORM-валідація? (Відповідь обґрунтуйте).
5. 💡 Мислення як у розробника
Як відрізнити новачка від профі в цій темі?
❌ Помилка новачка: Думати: "Я перевірив це на фронтенді в JavaScript, цього достатньо". Це фатальна помилка. Фронтенд можна відключити, змінити через F12 або відправити запит через Postman. Ніколи не довіряйте клієнту.
❌ Помилка новачка №2: Писати логіку валідації у "В'юхах" (Controllers/Views), а не в Моделях. Якщо ви перевіряєте дані в контролері, а потім хтось інший спробує створити об'єкт в іншому місці коду — перевірки не спрацюють.
🧠 Як думає профі (The "Fat Model" approach): "Я покладу правила перевірки якомога ближче до даних — у саму модель. Тоді, звідки б я не зберігав дані (через сайт, через адмінку, через імпорт файлу), правила завжди спрацюють автоматично".
Порада: Валідація ORM — це про бізнес-логіку (чи може бути ціна від'ємною?). Валідація БД (constraints) — це про структуру (чи може це поле бути NULL?). Використовуйте обидва рівні.
6. 🧩 Підсумок
Отже, друзі, що ми маємо в сухому залишку?
- Ми зрозуміли, що ORM валідація — це ваш перший рубіж оборони на сервері.
- Ми навчилися писати правила, які забороняють зберігати сміття в базі.
- Ми тепер знаємо, що валідація має жити поруч із даними (в моделях), а не розкидана по всьому проєкту.
Ви тепер вмієте: Захищати свою базу даних від некоректних даних, як професійний охоронець нічного клубу. Ваша база буде чистою, а код — надійним.
🔜 У наступній серії: Ми навчилися захищати дані кодом. Але як змусити саму Базу Даних гарантувати унікальність email або зв'язок між таблицями? На наступному уроці ми поговоримо про Міграції та Зовнішні ключі (Foreign Keys).
Це був CS50. Побачимось! 🎬