Модуль 12

QuerySet / Query API

Ось готовий урок, створений за твоїм майстер-промптом. Я обрав контекст Django ORM, оскільки термін QuerySet є візитною карткою саме цього фреймворку.


🎓 CS50-style: QuerySet та Query API (Django ORM)

Привіт, друзі! Радий бачити вас на занятті.

Сьогодні ми відкриваємо «чорну скриньку» нашого додатку. Ми вже вміємо створювати моделі (таблиці в базі даних), але який з них сенс, якщо ми не вміємо діставати звідти інформацію?

Уявіть, що ви — власник найбільшої бібліотеки у світі. У вас мільйони книг. І ось приходить відвідувач і каже: "Мені потрібна синя книга". Якби ви були комп'ютером без правильних інструментів, ви б пішли до першої полиці, взяли книгу, перевірили колір, поклали назад. Взяли другу... І так мільйон разів. Це довго. Це неефективно. Це, чесно кажучи, безглуздо.

Сьогодні ми навчимося бути розумним бібліотекарем. Ми розберемо QuerySet — інструмент, який дозволяє знаходити голку в копиці сіна за мілісекунди.

Готові? Поїхали! 🚀


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

Уявіть, що ви розробляєте інтернет-магазин. У вас у базі даних 100 000 товарів. Вам потрібно показати користувачеві: "Всі смартфони, дешевші за 20 000 грн, які є в наявності, відсортовані за рейтингом".

Риторичне запитання: Як би ви це робили без спеціальних інструментів? Писали б "сирий" SQL-запит?

SELECT * FROM products WHERE category_id = 5 AND price < 20000 AND stock > 0 ORDER BY rating DESC;

Виглядає ніби ок? А якщо я скажу, що назва таблиці змінилася? Або ви випадково допустили помилку в синтаксисі й «поклали» базу? Або, що найгірше, користувач через поле пошуку ввів шкідливий код (SQL Injection) і вкрав дані всіх ваших клієнтів?

Навіщо нам QuerySet? QuerySet (КверіСет) — це ваш адвокат і перекладач. Ви кажете йому мовою Python: "Дай мені смартфони", а він сам безпечно і максимально швидко пояснює це базі даних її мовою.

Без цього ви не зможете побудувати жоден динамічний сайт. Це міст між вашим кодом і даними.


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

Давайте розберемося, що це за звір.

Що таке QuerySet?

Це набір (список) об'єктів із вашої бази даних. Але це не просто список, як list у Python. Це розумний список.

Головний секрет: Лінивість (Laziness) 🦥

Це найважливіше, що треба зрозуміти сьогодні. QuerySet — лінивий.

Уявіть, що ви прийшли в ресторан і читаєте меню. 1. Ви дивитесь на "Борщ". 2. Ви дивитесь на "Вареники". 3. Ви дивитесь на "Узвар".

Чи почав кухар готувати борщ, поки ви просто читаєте рядок у меню? Ні. Кухар почне готувати тільки тоді, коли ви скажете офіціанту: "Неси!".

Так само працює QuerySet: * Ви можете створити запит: users = User.objects.filter(age=25) * Ви можете додати умову: users = users.exclude(is_active=False) * Ви можете відсортувати: users = users.order_by('name')

До цього моменту база даних навіть не знає про ваше існування. Жодного запиту не було відправлено. Запит полетить у базу тільки тоді, коли ви спробуєте використати дані: вивести їх на екран, запустити цикл for або взяти перший елемент.

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

Ви будуєте конструктор з Lego. Кожен метод (filter, all, exclude) додає кубик до інструкції. Коли ви нарешті просите дані, Django бере цю інструкцію, конвертує її в ідеальний SQL і надсилає в базу даних.

🔑 Ключові методи (запам'ятати):

  1. all() — дай мені все.
  2. filter(умова) — дай тільки те, що підходить (як сито).
  3. exclude(умова) — дай все, КРІМ цього.
  4. get(умова) — дай мені рівно один об'єкт (обережно з ним!).

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

Припустимо, у нас є модель Article (Стаття) з полями: title (назва), views (перегляди), is_published (чи опубліковано).

Приклад 1: "Дай усе"

# Запит
articles = Article.objects.all()
# Тут SQL ще не виконався! 🦥

# А ось тут виконується:
print(articles)

Що ми отримаємо? QuerySet, який виглядає як список усіх статей: <QuerySet [<Article: Hello>, <Article: World>]>.


Приклад 2: Фільтрація (Реальний кейс)

Нам треба показати на головній сторінці тільки опубліковані статті.

Питання до вас: Що буде, якщо ми не використаємо фільтр, а просто переберемо всі статті циклом if у Python? Відповідь: Ми завантажимо з бази тисячі чернеток, заб'ємо пам'ять сервера, і тільки потім Python їх відкине. Це повільно.

Робіть це на рівні бази:

# Правильно:
published_articles = Article.objects.filter(is_published=True)

Приклад 3: Ланцюжки (Chaining) ⛓️

Це найкрутіша фішка. Ми можемо нанизувати методи один на одного.

Задача: Знайти опубліковані статті, у яких понад 100 переглядів, і відсортувати їх від найпопулярніших.

top_content = Article.objects.filter(is_published=True).filter(views__gt=100).order_by('-views')

(Примітка: views__gt означає "Greater Than" — більше ніж).

Пояснення: Ми взяли весь список -> відсіяли неопубліковані -> відсіяли непопулярні -> посортували. І все це перетвориться в один ефективний SQL-запит.


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

Уяви, що ти працюєш з базою даних студентів: Student (поля: name, age, gpa (середній бал), faculty).

Твої завдання:

  1. 🔹 Розігрів: Напиши запит, щоб отримати всіх студентів факультету "Computer Science".
  2. 🔹 Ускладнюємо: Отримай студентів, яким 20 років І в яких середній бал (gpa) вище 3.5.
  3. 🔹 Виключення: Знайди всіх студентів, окрім тих, хто вчиться на "History".
  4. 🔹 Ланцюжок: Знайди топ-5 наймолодших студентів факультету "Math" (підказка: використовуй order_by та зрізи, як у списку [:5]).
  5. 🔹 Виправ помилку: python # Задача: знайти студента з ім'ям "Ivan" student = Student.objects.filter(name="Ivan") print(student.age) # ПОМИЛКА! Чому? Як виправити код, щоб отримати доступ до поля age?
  6. 🔹 Міні-кейс: Тобі дзвонить декан. Треба терміново список студентів на відрахування (GPA менше 2.0). Але давай їм останній шанс — не включай у цей список першокурсників (age < 18). Напиши цей запит.

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

Як відрізнити новачка від профі, дивлячись на код QuerySet?

🚫 Типова помилка новачка: N+1 проблема

Це класика. Дивіться:

# Погано 😱
books = Book.objects.all() # 1 запит
for book in books:
    print(book.author.name) # +1 запит ДЛЯ КОЖНОЇ книги!

Якщо книг 1000, ви зробите 1001 запит до бази. Ваша база «впаде», адмін буде злий. Як думає профі: "Мені треба витягнути дані одним махом". (Рішення: select_related — але це тема для окремого уроку, просто знайте, що така проблема існує).

🚫 Помилка: Python vs DB

Новачок: Витягує Book.objects.all(), перетворює в список Python і фільтрує через if. Профі: Максимально використовує filter() та exclude(). База даних написана на C++, вона оптимізована роками. Вона фільтрує швидше, ніж ваш Python-код. Довіряйте базі.

💡 Порада:

Завжди запитуйте себе: "Чи можу я відфільтрувати це ще до того, як дані потраплять у Python?" Якщо так — робіть це в QuerySet.


6. 🧩 Підсумок

Фух! Давайте видихнемо. Що ми сьогодні зрозуміли?

  1. QuerySet — це не дані, це інструкція. Він лінивий 🦥.
  2. Ми використовуємо методи all, filter, exclude, щоб будувати складні запити простими словами.
  3. Ми не пишемо SQL вручну (здебільшого), ми дозволяємо Django робити цю брудну роботу за нас.
  4. Чим більше роботи зробить база даних (фільтрація, сортування), тим швидше працює наш сайт.

Тепер ви вмієте: Знаходити потрібну інформацію в базі даних, не перебираючи кожен запис вручну. Ви — той самий розумний бібліотекар!

🕵️ Що далі? Зараз ми працювали з однією таблицею. Але світ складний. Студенти мають курси, автори мають книги. На наступному уроці ми дізнаємось, як пов'язувати таблиці між собою і робити магію з Relationships (зв'язками).

А поки — вперед до практики! Код сам себе не напише! 😉👨‍💻