Ось урок, створений спеціально для вас у стилі CS50. Вмикайте уяву, ми починаємо!
🎓 Урок: ORM у Django. Магія перекладу
Привіт, друзі! 👋
Сьогодні ми відкриваємо скриньку Пандори... але в хорошому сенсі! Ми поговоримо про те, як ваша Python-програма спілкується з базою даних.
1. 🔥 Вступ: Проблема та мотивація
Уявіть, що ви приїхали в Японію 🇯🇵. Ви хочете замовити в ресторані суп рамен. Але є проблема: ви говорите тільки англійською (або українською), а шеф-кухар розуміє тільки японську.
Що станеться, якщо ви почнете кричати йому на кухню: "Give me spicy ramen please!"? Він, швидше за все, просто знизає плечима. Ви залишитесь голодними.
У світі веб-розробки: * Ви (ваш код на Python) — це турист. * База даних (PostgreSQL, MySQL) — це шеф-кухар. Вона розуміє тільки мову SQL.
Раніше розробникам доводилося писати SQL-запити вручну прямо всередині Python-коду. Це виглядало приблизно так:
# 😱 Жах минулого
cursor.execute("SELECT * FROM users WHERE age > 18 AND status = 'active';")
Виглядає як текст, правда? А що, як ви помилилися в одній букві? А що, як ви вирішили змінити базу даних з MySQL на PostgreSQL? Вам довелося б переписувати весь цей код.
Тут на сцену виходить ORM (Object-Relational Mapping).
ORM — це ваш персональний перекладач. Ви кажете йому зрозумілою мовою Python: "Дай мені всіх активних користувачів", а він сам повертається до бази даних і каже їй на ідеальному SQL: SELECT * FROM ....
Навіщо нам це потрібно? 1. Швидкість: Ви пишете менше коду. 2. Безпека: ORM захищає вас від хакерських атак (SQL-ін'єкцій) автоматично. 3. Зручність: Ви працюєте з об'єктами Python, а не з таблицями.
2. 🧠 Теоретична база (без нудних лекцій)
Давайте зазирнемо під капот. Що таке ORM фізично?
Це шар коду, який перетворює: 1. Клас (Model) у вашому коді $\rightarrow$ на Таблицю в базі даних. 2. Екземпляр класу (Instance) $\rightarrow$ на Рядок (запис) у таблиці. 3. Атрибут класу (Field) $\rightarrow$ на Колонку таблиці.
🖼 Аналогія з Excel
Уявіть Excel-таблицю під назвою "Книги" (Book).
- Заголовки стовпців (Назва, Автор, Рік) — це поля вашої моделі в Django.
- Кожен рядок з даними про конкретну книгу — це об'єкт (екземпляр) цієї моделі.
Як це працює?
Коли ви пишете:
Book.objects.all()
Django "під капотом" робить:
1. Генерує SQL: SELECT * FROM library_book;
2. Відправляє його в базу.
3. Отримує "сирі" дані.
4. Магія: Перетворює ці дані назад у красиві Python-об'єкти, з якими ви можете працювати через крапку (book.title).
💡 Запам'ятайте головне: Ми не думаємо про "рядки" і "колонки". Ми думаємо про об'єкти та їх властивості.
3. 🧪 Приклади (від простого до реального)
Припустимо, у нас є модель для інтернет-магазину книг:
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
price = models.IntegerField()
is_bestseller = models.BooleanField(default=False)
Приклад 1: Створення (INSERT)
Як додати книгу в базу?
Ви пишете:
book = Book(title="Кобзар", author="Т. Шевченко", price=350)
book.save()
Що очікуєте? Що книга з'явиться в базі.
Реальність: Саме так! Метод .save() — це команда "Записати зміни в базу". Без неї об'єкт існує тільки у пам'яті Python.
Приклад 2: Пошук (SELECT + WHERE)
Нам потрібні всі книги, які коштують дешевше 400 гривень.
Ви пишете:
cheap_books = Book.objects.filter(price__lt=400)
(Зверніть увагу на __lt — це означає "less than", менше ніж).
Питання до вас: Що поверне cheap_books? Одну книгу? Список?
Відповідь: Це поверне QuerySet (схоже на розумний список) об'єктів. Якщо книг немає — він буде порожнім, але помилки не буде.
Приклад 3: Ланцюжок фільтрів (Chaining)
А тепер складніше. Хочемо книги Шевченка, які є бестселерами.
Ви пишете:
great_books = Book.objects.filter(author="Т. Шевченко").filter(is_bestseller=True)
# Або коротше:
great_books = Book.objects.filter(author="Т. Шевченко", is_bestseller=True)
Чому це круто? Django ще не зробив запит до бази даних. Він "лінивий" (lazy). Він чекає, поки ви справді не захочете прочитати ці дані (наприклад, вивести їх на екран). Тільки тоді він піде в базу. Це економить ресурси!
4. 🛠 Практична частина
Час розім'яти пальці! Уявіть, що ви працюєте над backend-частиною Netflix. У вас є модель Movie:
title (назва), year (рік), rating (рейтинг 1-10), genre (жанр).
Виконуємо завдання (можна писати в уявному shell):
- 🔹 Створіть хіт: Створіть фільм "Inception", 2010 року, рейтинг 9, жанр "Sci-Fi". Не забудьте зберегти!
- 🔹 Знайдіть класику: Напишіть запит, щоб отримати всі фільми, випущені до 2000 року (
year__lt=2000). - 🔹 Виправте помилку: Ви випадково поставили фільму "The Room" рейтинг 10. Знайдіть цей фільм (використовуйте
.get(title="The Room")), змініть рейтинг на 2 і збережіть зміни. - 🔹 Реальна задача: Менеджер просить список усіх "Комедій" (
Comedy) з рейтингом вище 7. Як це зробити одним рядком? - 🔹 Міні-кейс: Вам треба видалити всі фільми з рейтингом менше 3. Як це зробити, не видаляючи їх по одному в циклі? (Підказка: у QuerySet є метод
.delete()). - 🔹 Питання "А що, якщо...": Ви спробуєте зробити
Movie.objects.get(year=2020), але у вас є два фільми 2020 року. Що зробить Django? (Спробуйте здогадатися: поверне перший? Поверне помилку?)
5. 💡 Мислення як у розробника
Як відрізнити новачка від профі в роботі з ORM?
❌ Помилка новачка: Проблема N+1
Новачок пише так:
# Погано!
books = Book.objects.all() # 1 запит
for book in books:
print(book.author.name) # +1 запит для КОЖНОЇ книги!
Якщо у вас 100 книг, ви зробите 101 запит до бази. База даних "впаде" від навантаження. Це як ходити в магазин окремо за кожним яйцем.
✅ Як думає профі:
"Я заберу всі дані за один раз".
# Добре!
books = Book.objects.select_related('author').all()
Django зробить складний JOIN у SQL і дістане все одним запитом. Ви купили лоток яєць одразу.
2 Поради з практики:
.get()небезпечний. Якщо об'єкт не знайдено або їх більше одного — програма впаде з помилкою. Використовуйте його тільки тоді, коли впевнені на 100% (наприклад, пошук по ID), і загортайте в блокtry/except.- Не бійтеся дивитися SQL. Django дозволяє подивитися, який саме SQL він згенерував (
print(queryset.query)). Іноді перекладач може зрозуміти вас неправильно!
6. 🧩 Підсумок
Отже, що ми сьогодні дізналися?
- ORM — це ваш перекладач з Python на SQL.
- Model — це креслення таблиці.
- QuerySet — це список результатів, який "лінивий" і виконується тільки за потреби.
- Ми вміємо створювати (
.save()), фільтрувати (.filter()), отримувати (.get()) та оновлювати дані.
Що ви тепер вмієте? Ви можете маніпулювати даними в базі, не написавши жодного рядка SQL. Ви володієте магією абстракції!
🔮 Тизер наступного уроку
Сьогодні ми працювали з однією таблицею. Але світ складний. Книги мають авторів. Автори мають біографії. Користувачі мають кошики покупок.
На наступному уроці ми з'єднаємо ці таблиці між собою. Ми поговоримо про Зв'язки (Relationships): ForeignKey та ManyToMany. Як пов'язати Гаррі Поттера з Джоан Роулінг?
Це було CS50... тобто, наш урок по Django! До зустрічі! 💻🚀