Какие основные методы Django QuerySet используются для получения и фильтрации данных?

Ответ

Объект QuerySet в Django представляет собой коллекцию объектов из базы данных. Его ключевая особенность — ленивость (laziness): запрос к базе данных выполняется только в момент реального обращения к данным (например, в цикле или при вызове list()).

Основные методы можно разделить на две категории:

1. Методы, возвращающие новый QuerySet (не выполняют запрос к БД)

Эти методы можно выстраивать в цепочки для создания сложных запросов.

  • filter(**kwargs): Возвращает QuerySet, содержащий объекты, которые соответствуют заданным параметрам поиска.
  • exclude(**kwargs): Возвращает QuerySet, исключающий объекты, которые соответствуют параметрам.
  • order_by(*fields): Сортирует QuerySet по указанным полям. Префикс - означает сортировку по убыванию (например, '-created_at').
  • annotate(**kwargs): Добавляет к каждому объекту в QuerySet вычисляемое поле (например, с помощью агрегатных функций Count, Sum).
  • values(*fields): Возвращает QuerySet, который при итерации выдает словари, а не объекты моделей.
  • all(): Возвращает копию текущего QuerySet.

2. Методы, выполняющие запрос к БД (возвращают данные)

Эти методы вызывают обращение к базе данных и возвращают конкретный результат.

  • get(**kwargs): Возвращает один-единственный объект. Если найдено 0 или более 1 объекта, вызывает исключение (DoesNotExist или MultipleObjectsReturned).
  • count(): Возвращает количество объектов в QuerySet.
  • first() / last(): Возвращают первый/последний объект в QuerySet или None, если он пуст.
  • exists(): Возвращает True, если QuerySet содержит хотя бы один объект, иначе False. Более эффективен, чем count() > 0.

Пример цепочки вызовов:

from django.db.models import Count
from .models import Post

# Найти 10 самых популярных постов в категории 'Python',
# опубликованных в этом году, отсортированных по количеству комментариев

popular_posts = (
    Post.objects
    .filter(category__name='Python', pub_date__year=2023)  # Фильтрация
    .annotate(num_comments=Count('comments'))  # Добавление вычисляемого поля
    .order_by('-num_comments')  # Сортировка по убыванию
    [:10]  # Ограничение выборки (срез)
)

# Запрос к БД будет выполнен только здесь, при итерации
for post in popular_posts:
    print(f'{post.title} - Комментариев: {post.num_comments}')

Ответ 18+ 🔞

А, вот эта штука, QuerySet! Ну, слушай, это же основа основ, блядь. Представь себе, что это такой ленивый засранец, который только делает вид, что работает. Сидит, блядь, на диване, обещает тебе горы данных, а сам нихуя не делает, пока ты его реально не ткнёшь носом в результат. Ленивость, ёпта, святое дело!

Так, основные методы, они как бы на две банды делятся.

Первая банда — это те, кто только строит планы, но нихуя не делает.
Типа, filter() — отберёт тебе только то, что нужно. exclude() — наоборот, выкинет всё лишнее, как говно из ванны. order_by() — расставит всё по полочкам, по алфавиту или наоборот, если поставишь минус. annotate() — это вообще хитрая жопа, она каждому объекту новые поля пририсует, типа «а вот смотри, у этого поста сколько там комментариев». И всё это можно в цепочку собирать, как паровозик, и они будут друг другу кивать: «Ага, понял, отфильтрую, потом отсортирую, потом посчитаю». А запрос в базу? Нихуя! Пока не спросишь по-взрослому.

Вторая банда — это те, кто сразу лезет в драку.
Вот get() — это такой отчаянный мудак, который ищет ОДИН объект. Найдёт — молодец. Не найдёт — орёт «DoesNotExist!». Найдёт два — орет «MultipleObjectsReturned!». Пиздец просто.
count() — посчитает, сколько всего. first() и last() — вытащат первого и последнего, если есть. А exists() — это вообще гениально, он просто смотрит, есть хоть что-то или нет, и говорит «да» или «нет». Быстрее, чем считать всё дохуя, честно.

Вот смотри, как это в жизни выглядит, на примере блога:

from django.db.models import Count
from .models import Post

# Хотим найти 10 самых популярных постов про Python за этот год
popular_posts = (
    Post.objects
    .filter(category__name='Python', pub_date__year=2023)  # Отфильтровали
    .annotate(num_comments=Count('comments'))  # Присобачили к каждому счётчик комментариев
    .order_by('-num_comments')  # Отсортировали по убыванию, чтобы самые болтливые сверху
    [:10]  # Отрезали топ-10, как торт
)

# И вот тут, сука, только сейчас этот ленивый уёбок полезет в базу данных!
# Когда ты начнёшь по этому списку итерироваться.
for post in popular_posts:
    print(f'{post.title} - Комментариев: {post.num_comments}')

Вот и вся магия. Сначала накручиваешь условия, как на ёлку гирлянды, а потом — раз! — и включаешь. И если что-то пошло не так, всегда можно крикнуть: «Да что ж ты, блядь, get()-то вызвал, когда их там три штуки?!».