Ось готовий урок, згенерований у стилі Девіда Малана (CS50), спеціально для теми QuerySet у Django.
🎓 CS50-Style: Магія QuerySet та робота з даними
Привіт усім! 👋 Ласкаво просимо назад.
Сьогодні ми не просто пишемо код. Сьогодні ми вчимося бути володарями даних.
1. 🔥 Вступ: Проблема та мотивація
Уявіть, що ви — власник величезного супермаркету. У вас на складі лежить мільйон товарів. 📦
До вас підходить клієнт і каже: "Я хочу купити червоні кросівки 42-го розміру".
Що ви зробите? 1. Підете на склад і будете перебирати кожен з мільйона товарів у руках, перевіряючи: "Це кросівки? Ні. Це кросівки? Так. А вони червоні? Ні..."? 2. Чи, можливо, ви заглянете в комп’ютерну базу, відфільтруєте список і підете до конкретної полиці?
Очевидно, другий варіант, правда?
Але в програмуванні новачки часто роблять саме перший варіант! Вони витягують з бази даних ВСЕ (усіх користувачів, усі товари), а потім у циклі Python намагаються знайти потрібне. Це повільно. Це вбиває пам'ять вашого сервера. Це — катастрофа для стартапу, який росте. 📉
Ось тут на сцену виходить QuerySet.
Нам потрібен спосіб сказати базі даних: "Гей, дай мені тільки те, що мені треба, і відсортуй це, будь ласка, за ціною", ще до того, як дані потраплять у нашу програму.
Сьогодні ми розберемося, як це робити елегантно, швидко і професійно. Поїхали! 🚀
2. 🧠 Теоретична база: Що там "під капотом"?
Давайте спростимо.
У Django є Model (опис таблиці) і є Manager (зазвичай це .objects).
Коли ви пишете Product.objects..., ви звертаєтесь до "менеджера", який керує доступом до даних.
А що повертає менеджер? Він повертає QuerySet.
📝 Визначення: QuerySet — це колекція об'єктів із вашої бази даних. Але це не просто список (list). Це — лінивий запит.
🐢 Магія "Лінивості" (Lazy Execution)
Це найважливіше, що треба зрозуміти сьогодні.
Уявіть, що ви в ресторані. 🍽️ Ви читаєте меню. Ви думаєте: "Може, замовити суп? А може, салат? Ні, давайте стейк". Поки ви це думаєте і навіть коли кажете офіціанту "Я буду стейк..." — кухар ще нічого не готує. Кухня починає працювати тільки тоді, коли офіціант відносить замовлення і каже: "ГОТУЙ!".
Так само працює QuerySet:
1. Ви створюєте фільтр (filter).
2. Додаєте сортування (order_by).
3. Виключаєте зайве (exclude).
У цей момент Django НЕ чіпає базу даних. Він просто формує "замовлення" (SQL-запит) у пам'яті. Запит полетить у базу тільки тоді, коли ви реально захочете побачити дані (наприклад, виведете їх у шаблоні або у print()).
Чому це круто? Це економить ресурси. Ви можете будувати складні ланцюжки фільтрів, не навантажуючи базу на кожному кроці.
3. 🧪 Приклади: Від простого до реального
Давайте працювати з моделлю Student. У студента є ім'я (name), вік (age) і середній бал (gpa).
Приклад 1: Отримати всіх
Найпростіше.
students = Student.objects.all()
❓ Питання до вас: Чи зробив Django запит до бази в цьому рядку? Ні! Він просто підготував "намір" отримати всіх.
Приклад 2: Фільтрація (The Filter)
Нам потрібні тільки відмінники (GPA > 90).
smart_students = Student.objects.filter(gpa__gt=90)
Зверніть увагу: gpa__gt означає "gpa greater than" (більше ніж). Це спеціальний синтаксис Django.
Приклад 3: Ланцюжки (Chaining) ⛓️
А тепер реальний кейс. Нам потрібні студенти: 1. Старші 18 років. 2. Які НЕ звуться "Ivan" (ну, наприклад). 3. Відсортовані за прізвищем.
Як це напише новачок? Можливо, трьома різними командами. Як це напише профі?
result = Student.objects.filter(age__gte=18).exclude(name="Ivan").order_by("name")
👀 Що тут відбувається? Ми беремо QuerySet, застосовуємо фільтр — отримуємо новий QuerySet. Застосовуємо виключення — знову новий QuerySet. Це як конвеєр на заводі.
І тільки коли ми напишемо:
for s in result:
print(s.name)
...ось у цей момент Django скаже базі даних: "SELECT * FROM students WHERE..." і принесе дані.
4. 🛠 Практична частина
Час розім'яти пальці! Уявіть, що ви працюєте над системою для інтернет-магазину. У вас є модель Order (Замовлення) з полями:
* status ('new', 'paid', 'shipped')
* amount (сума замовлення)
* created_at (дата створення)
Завдання 1: Розігрів
Напишіть запит, щоб отримати всі замовлення зі статусом 'paid'.
Завдання 2: Знайди "китів" 🐳
Напишіть запит, щоб знайти замовлення, які є 'paid' І сума яких перевищує 1000.
Завдання 3: Виправ помилку 🐛
Студент-стажер написав код, щоб знайти конкретне замовлення за ID=1, але код падає з помилкою, якщо замовлення не існує.
# Код стажера:
order = Order.objects.get(id=1)
Як зробити це безпечніше або обробити помилку? (Підказка: згадайте try/except або filter + first()).
Завдання 4: Міні-кейс
Менеджер каже: "Дай мені 5 найсвіжіших замовлень, які ще не відправлені (статус 'new')".
Як ви це реалізуєте, використовуючи filter, order_by (по спаданню дати) і зрізи Python [:5]?
Завдання 5: А що, якщо...
Ви написали orders = Order.objects.all(). А потім у коді зробили print(orders[0]).
Скільки записів витягне Django з бази? Один чи всі? (Це питання з зірочкою ⭐).
5. 💡 Мислення як у розробника
Як відрізнити код джуніора від коду сеньйора в цій темі?
🚫 Помилка №1: Фільтрація Python-ом
Погано:
users = User.objects.all() # Витягли 10 000 користувачів у пам'ять
active_users = []
for u in users:
if u.is_active: # Перевіряємо в циклі
active_users.append(u)
Це повільно і з'їдає всю оперативну пам'ять (RAM).
Добре:
active_users = User.objects.filter(is_active=True) # База сама все відфільтрувала
🚫 Помилка №2: len() проти count()
Якщо вам треба просто знати кількість, не робіть len(Model.objects.all()). Це завантажить усі об'єкти, щоб просто їх порахувати.
Використовуйте Model.objects.count(). Це перетвориться на швидкий SQL-запит SELECT COUNT(*).
Порада від профі:
QuerySet'и можна зберігати у змінних і передавати у функції. Вони не "псуються". Ви можете створити "базовий запит" (наприклад, тільки активні товари) і використовувати його в різних частинах сайту, дофільтровуючи за потребою.
6. 🧩 Підсумок
Отже, що ми сьогодні поклали в нашу "скарбничку знань"?
- QuerySet — це розумний список об'єктів.
- Він лінивий 🦥. Він не біжить у базу, поки ви його не змусите.
- Ми використовуємо Chaining (ланцюжки), щоб писати красиві та складні запити:
filter().exclude().order_by(). - Ми перекладаємо важку роботу на Базу Даних, а не робимо її в Python.
Ви тепер вмієте: Отримувати саме ті дані, які вам потрібні, не навантажуючи сервер зайвою роботою.
🔎 Тизер наступного уроку:
А що робити, якщо дані розкидані по різних таблицях? Як отримати всі замовлення конкретного користувача? На наступному уроці ми розберемо Relational Fields (Зв'язки) та магічний select_related. Це буде як збирати LEGO з різних коробок!
А поки що — це був CS50. Побачимось! 👋