Модуль 11

CRUD-операції через ORM

Ось твій урок. Приготуй попкорн, зараз будемо розбиратися з базами даних так, ніби ми просто замовляємо піцу. 🍕


🎓 Урок: CRUD-операції через ORM. Магія перетворення коду в дані

Привіт, друзі! Це CS50 (умовно 😉), і сьогодні ми поговоримо про те, як припинити писати нудні SQL-запити вручну і почати спілкуватися з базою даних мовою вашого коду.

Сьогоднішня тема — CRUD через ORM. Звучить страшно? Тільки якщо не знати, що це.


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

Уявіть ситуацію. Ви пишете сучасний веб-сайт, скажімо, "вбивцю Instagram". У вас є користувачі, пости, лайки. Все це треба десь зберігати.

Звичайно, ви можете використовувати SQL (Structured Query Language). Це як розмовляти з комп'ютером мовою стародавніх сувоїв.

Щоб додати користувача, ви пишете:

INSERT INTO users (name, email, age) VALUES ('Oleh', 'oleh@example.com', 25);

Але що, якщо ви зробите друкарську помилку? Напишете INSER замість INSERT? Або забудете закрити лапку? Програма впаде. А якщо ви захочете змінити базу даних з MySQL на PostgreSQL? Доведеться переписувати купу коду.

Риторичне питання: Хіба не було б круто, якби ми могли працювати з базою даних так само просто, як зі звичайними об’єктами в коді? Наприклад, просто створити об'єкт User, і він магічним чином зберігся б у базі?

Ось тут на сцену виходить ORM (Object-Relational Mapping).

Аналогія: Уявіть, що SQL — це керування автомобілем через розбирання двигуна і ручне впорскування палива. А ORM — це кермо і педалі. Ви тиснете педаль (метод у коді), а машина сама знає, скільки бензину (SQL-запитів) треба впорснути.

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


2. 🧠 Теоретична база (без занудства)

Що таке CRUD? Це чотири вершники роботи з даними. Це все, що ми робимо з інформацією: 1. Create (Створити) — додати запис. 2. Read (Прочитати) — знайти запис. 3. Update (Оновити) — змінити запис. 4. Delete (Видалити) — стерти запис.

А що таке ORM? Це ваш перекладач. * Ви кажете мовою Python/Java/C#: "Створи нового студента Девіда". * ORM перекладає це для бази даних: "INSERT INTO students...".

⚙️ Як це працює "під капотом"?

Це найважливіша концепція, яку треба зрозуміти інтуїтивно:

  1. Клас (Class) = Таблиця (Table). Якщо у вас є клас User, у базі даних буде таблиця users.
  2. Об’єкт (Instance) = Рядок (Row). Один конкретний new User("David") — це один рядок у таблиці.
  3. Атрибут (Attribute) = Колонка (Column). user.name — це колонка name.

👉 Що треба запам’ятати: Ви маніпулюєте об'єктами, а ORM маніпулює рядками таблиці за вас. Ви змінюєте змінну в коді — ORM робить UPDATE у базі.


3. 🧪 Приклади (від простого до реального)

Давайте використаємо псевдокод, схожий на Python (SQLAlchemy/Django), бо він найбільш читабельний.

Уявіть, у нас є модель (клас) Student.

🔹 Приклад 1: Create (Створення)

Без ORM (SQL):

INSERT INTO students (name, house) VALUES ('Harry', 'Gryffindor');

З ORM:

# Ми просто створюємо об'єкт!
new_student = Student(name="Harry", house="Gryffindor")
db.session.add(new_student) # Додаємо в "кошик"
db.session.commit()         # Натискаємо "Зберегти"

Запитання до вас: Що станеться, якщо я напишу перші два рядки, але забуду commit()? Відповідь: Нічого! База даних не зміниться. Це як написати лист, але не кинути його в поштову скриньку. Транзакція не завершена.


🔹 Приклад 2: Read (Читання)

Ми хочемо знайти всіх студентів з Грифіндору.

З ORM:

gryffindor_students = Student.query.filter_by(house="Gryffindor").all()

Бачите? Ми читаємо це як звичайне англійське речення: "Student query filter by house...". Нам не треба думати про SELECT * FROM....


🔹 Приклад 3: Update (Оновлення) — Реальний кейс

Гаррі одружився і змінив прізвище (ну, припустімо 😉). Або просто змінив гуртожиток.

# 1. Спочатку треба знайти Гаррі
harry = Student.query.filter_by(name="Harry").first()

# 2. Змінюємо атрибут прямо як звичайну змінну
harry.house = "Slytherin"  # О ні! 🐍

# 3. Фіксуємо зміни
db.session.commit()

Запитання: Чи треба нам писати SQL-запит UPDATE? Відповідь: Ні! ORM "стежить" за об'єктом harry. Вона бачить, що ви змінили поле house, і сама згенерує потрібний SQL, коли ви викличете commit().


🔹 Приклад 4: Delete (Видалення)

Гаррі виключили зі школи.

student_to_delete = Student.query.filter_by(name="Harry").first()
db.session.delete(student_to_delete)
db.session.commit()

Просто, елегантно, чисто.


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

Час забруднити руки кодом! Уявіть, що ви розробляєте бекенд для інтернет-магазину піци. У вас є модель Pizza(name, price, size).

Виконуйте завдання подумки або на листочку (псевдокод підійде):

  1. Повторення: Напишіть код, який створює піцу "Margarita" ціною 150 грн розміром "L" і зберігає її в базі.
  2. Зміна умов (Read): Як отримати список всіх піц, які коштують менше 200 грн? (Підказка: використовуйте логіку filter(Pizza.price < 200)).
  3. Виправ помилку: Студент написав такий код для зміни ціни: python pizza = Pizza.query.get(1) pizza.price = 300 # Чого тут не вистачає?
  4. Реальна задача (Update): Інфляція! Напишіть цикл, який проходить по всіх піцах у базі і піднімає ціну кожної на 10%. Не забудьте зберегти!
  5. Міні-кейс: До нас прийшов клієнт і каже: "Видаліть з меню всі піци з ананасами". Напишіть алгоритм: Знайти -> Видалити -> Зберегти.
  6. "А що, якщо...": Ви створили об'єкт pizza, але не задали price (а в базі це поле обов'язкове). В який момент програма видасть помилку: коли ви створили об'єкт чи коли зробили commit()?

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

Ось де ми переходимо від новачків до профі.

⚠️ Типова помилка новачка: "Проблема N+1"

Ви хочете вивести список студентів і їхні гуртожитки. Новачок робить так: 1. Запитує список всіх студентів (1 запит). 2. У циклі для кожного студента робить запит до бази, щоб отримати назву гуртожитку.

Якщо у вас 100 студентів, ви зробите 101 запит до бази! Це повільно. Як думає профі: "Я маю витягнути всі дані одним складним запитом (JOIN), навіть якщо використовую ORM". (ORM це вміє, але треба їй сказати).

🛡 Безпека

Пам'ятаєте SQL-ін’єкції? Коли хакер вводить у поле логіну ' OR '1'='1 і зламує сайт? ORM автоматично екранує (знешкоджує) дані. Порада: Завжди використовуйте методи ORM, а не склеюйте SQL-рядки вручну ("SELECT * FROM users WHERE name = " + user_input). Це шлях до катастрофи.

🕵️‍♂️ Не вір магії наосліп

ORM — це зручно, але це абстракція. Іноді вона генерує дуже повільні SQL-запити. Порада профі: Час від часу вмикайте режим "показати SQL", який генерує ваша ORM, і перевіряйте, чи не робить вона дурниць.


6. 🧩 Підсумок

Отже, що ми сьогодні зробили? Ми навчилися керувати даними, не стаючи експертами з SQL.

  • Ви знаєте, що Клас = Таблиця, а Об'єкт = Рядок.
  • Ви вмієте робити C.R.U.D. методами коду.
  • Ви розумієте, що commit() — це кнопка "Зберегти гру".

Що далі? Зараз наші таблиці (студенти, піци) живуть окремо, як острови в океані. Але світ зв'язаний! Студенти належать до гуртожитків. Піци входять у замовлення. На наступному уроці ми поговоримо про Відносини (Relationships): One-to-One, One-to-Many і страшний Many-to-Many. Як зв'язати таблиці між собою, не втративши розум?

А на сьогодні це все. Це був CS50. Побачимось! 👋