Django ORM, методы возвращающие QuerySet

⏱ 30 мин · 🧩 заданий: 7

Методы, возвращающие QuerySet

Эти методы не выполняют запрос сразу, а возвращают новый QuerySet с добавленным условием. Поэтому их можно соединять в цепочки в любом порядке — Django соберёт из цепочки один SQL-запрос.

Book.objects.filter(pages__gt=100).exclude(author__name="Чехов").order_by("-published")[:10]

Фильтрация: filter() и exclude()

Book.objects.filter(author__name="Толстой", pages__gt=500)    # AND
Book.objects.filter(author__name="Толстой").filter(pages__gt=500)  # тоже AND
Book.objects.exclude(pages__lt=100)                             # NOT

Для OR используют объекты Q:

from django.db.models import Q

Book.objects.filter(Q(author__name="Толстой") | Q(author__name="Чехов"))
Book.objects.filter(Q(pages__gt=500) & ~Q(author__name="Толстой"))  # ~ — НЕ

Сортировка: order_by() и reverse()

Book.objects.order_by("title")             # по возрастанию
Book.objects.order_by("-pages", "title")   # по убыванию страниц, затем по названию
Book.objects.order_by("author__name")      # по полю связанной модели
Book.objects.order_by("?")                 # случайно (медленно на больших таблицах)
Book.objects.order_by("title").reverse()   # обратный порядок

order_by() без аргументов отменяет сортировку из Meta.ordering.

Уникальность: distinct()

Author.objects.filter(books__pages__gt=500).distinct()

Без distinct() автор с тремя толстыми книгами попал бы в результат трижды (из-за JOIN).

Словари и кортежи: values() и values_list()

Когда объекты моделей не нужны, а нужны только данные:

Book.objects.values("title", "pages")
# <QuerySet [{'title': 'Война и мир', 'pages': 1300}, ...]>

Book.objects.values_list("title", "pages")
# <QuerySet [('Война и мир', 1300), ...]>

Book.objects.values_list("title", flat=True)
# <QuerySet ['Война и мир', 'Анна Каренина', ...]>

Book.objects.values("author__name")   # можно брать поля через связи

Работает быстрее: Django не создаёт объекты моделей.

# Плохо: 1 запрос за книгами + N запросов за авторами
for book in Book.objects.all():
    print(book.title, book.author.name)

# Хорошо: один запрос с JOIN
for book in Book.objects.select_related("author"):
    print(book.title, book.author.name)
Метод Для каких связей Как работает
select_related ForeignKey, OneToOne (прямая сторона) SQL JOIN, один запрос
prefetch_related ManyToMany, обратные связи Отдельный запрос + склейка в Python
Post.objects.select_related("author").prefetch_related("tags", "comments")

Вычисляемые поля: annotate()

annotate добавляет к каждому объекту поле, вычисленное в базе:

from django.db.models import Count, Avg

authors = Author.objects.annotate(books_count=Count("books"))
for a in authors:
    print(a.name, a.books_count)

Author.objects.annotate(avg_pages=Avg("books__pages")).filter(avg_pages__gt=300)

По аннотированному полю можно фильтровать и сортировать.

Частичная загрузка: only() и defer()

Book.objects.only("title")       # загрузить только title (и id)
Book.objects.defer("description") # загрузить всё, кроме тяжёлого поля

Обращение к незагруженному полю вызовет дополнительный запрос — используйте осознанно.

Даты: dates() и datetimes()

Post.objects.dates("publish", "month")
# список первых дней месяцев, в которые есть посты — удобно для архива блога

Объединение запросов

qs1 = Book.objects.filter(pages__lt=100)
qs2 = Book.objects.filter(pages__gt=1000)
qs1.union(qs2)          # UNION
qs1.intersection(qs2)   # INTERSECT
qs1.difference(qs2)     # EXCEPT
qs1 | qs2               # OR условий

Прочие полезные

Book.objects.none()          # пустой QuerySet (без запроса к БД)
Book.objects.all()           # копия QuerySet
Book.objects.filter(...).select_for_update()  # блокировка строк в транзакции
Book.objects.using("replica")                 # другая база данных

Срезы

Book.objects.all()[:5]      # LIMIT 5
Book.objects.all()[5:10]    # OFFSET 5 LIMIT 5

Отрицательные индексы не поддерживаются. Срез тоже возвращает QuerySet (если без шага).

Итог

  • Эти методы ленивые и комбинируются цепочкой в один запрос.
  • filter/exclude + Q для сложных условий, order_by для сортировки.
  • values/values_list — данные без объектов моделей.
  • select_related (JOIN) и prefetch_related (отдельный запрос) решают проблему N+1.
  • annotate добавляет вычисляемые поля.

Практика решено 0 из 7

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Несколько вариантов

Какие из этих методов возвращают новый QuerySet (их можно продолжать цепочкой)?

Задание 2 Написать код

Получите список только названий всех книг, отсортированных по алфавиту, в виде плоского списка строк (не кортежей). Результат — в titles.

Задание 3 Один вариант ответа

Что выбрать, чтобы при выводе списка книг с именами авторов (ForeignKey) не делать запрос на каждого автора?

Задание 4 Написать код

Получите авторов с дополнительным полем books_count — числом их книг (обратная связь books), отсортированных по убыванию этого числа. Результат — в authors.

Задание 5 Один вариант ответа

Что вернёт Book.objects.values("title", "pages")?

Задание 6 Ввод ответа

Какой метод QuerySet убирает повторяющиеся строки из результата (SQL DISTINCT)?

Задание 7 Расставить по порядку

Как будет отсортирован результат Book.objects.order_by("-pages", "title")? Расставьте книги.

  1. «Бесы», 860 стр.
  2. «Война и мир», 1300 стр.
  3. «Анна Каренина», 860 стр.
  4. «Муму», 40 стр.

Войдите, чтобы отмечать пройденные уроки.