Перейти к содержанию

values() и values_list()

.values() и .values_list() возвращают полноценный запрос, выдающий словари или кортежи (отдельные значения, именованные кортежи). Каждый метод в цепочке применяет тот же вызов .values() к новому запросу — qs.values("a").filter(x=1) в точности то же, что qs.filter(x=1).values("a"), — поэтому всё поведение запроса (правила срезов, HAVING, курсоры, автоматические ограничения арендатора и мягкого удаления, выбор подключения) сохраняется. Методы в цепочке используют имена полей и вычисляемых значений, как у запроса; выражение, переданное в values(name=expression)/values_list(name=expression), тоже доступно по этому имени, как и переименованное поле: values(title="name").filter(title="a").order_by("-title") (новое имя, совпадающее с полем модели, по-прежнему означает это поле).

Метод Тип Что делает
filter() / exclude() / order_by() / distinct(*fields) / alias() / group_by() / all() / none() / limit() / offset() / using() / select_for_update() / with_cte() / after_cursor() / before_cursor() / all_tenants() / include_deleted() / only_deleted() цепочка Как у запроса, сохраняя выбранные поля и вид строки. Фильтр по агрегатному вычисляемому значению применяется к группам (HAVING).
annotate(**kwargs) цепочка Добавляет вычисляемые значения к выбранным колонкам (после них) — см. annotate()
values(...) / values_list(...) цепочка Заменяет выбранные поля (и словари на кортежи, и наоборот). Запрос, сгруппированный агрегатом по выбранным полям, остаётся сгруппированным по ним: values("author_id").annotate(n=Count("id")).values_list("n", flat=True) — число на каждого автора.
qs[a:b] / qs[i] цепочка / конечный Срез складывается с уже взятым срезом; индекс — одна строка в этой позиции; если её нет, при await будет IndexError; отрицательный индекс даёт QueryError.
count() await Число строк, которое возвращает await qs: групп у сгруппированного запроса, различных строк у запроса с .distinct(), с учётом среза; считается по запросу как по производной таблице.
exists() await Вернёт ли await qs хотя бы одну строку, с учётом среза.
aggregate(**kwargs) await Если каждая строка — строка исходного запроса (нет группировки, нет .distinct() по выбранным полям, нет среза, нет выбранного поля или сортировки через связь «ко многим»), это aggregate() самого запроса, и он может читать любое поле — см. aggregate()
first() / last() конечный Первая/последняя строка в сортировке; без сортировки сгруппированный запрос упорядочен по ключу группировки (выбранные не агрегатные поля или поля .group_by()), любой другой — по первичному ключу.
earliest(*fields) / latest(*fields) / get(*args, does_not_exist_exception=..., multiple_objects_returned_exception=..., **kwargs) конечный Как у запроса, возвращая строку. На запросе со срезом, строки которого не соответствуют строкам модели один к одному (сгруппированные, с .distinct() или размноженные связью «ко многим»), они и last() дают QueryError: срез взят по строкам, которые запрос не может сузить.
iterator(chunk_size=1000) асинхронный генератор Перебирает строки страницами — см. iterator()
stream(chunk_size=1000) асинхронный генератор Курсор, как QuerySet.stream(), — внутри транзакции.
union(*others, all=False) / intersection(*others) / difference(*others) цепочка QuerySet объединённых строк — см. ниже.
update(**kwargs) await Обновляет строки исходного запроса, выбранные поля роли не играют. QueryError на сгруппированном запросе (его строки — группы, а не строки модели) и, как в Django, после среза, offset() или distinct(*fields).
delete() / contains() / only() / defer() / select_related() / prefetch_related() — QueryError — вызывайте их у запроса до .values().
sql() / explain() синхронно / async def Как у запроса.

annotate()

Добавляет вычисляемые значения к выбранным колонкам (после них). Агрегат группирует строки по остальным выбранным колонкам: Book.objects.all().values("author_id").annotate(n=Count("id")) возвращает строку на автора. Порядок важен: агрегат, вычисленный до values()/values_list() (в том числе через .alias()) или переданный им именованным аргументом, считается для каждой строки модели — Author.objects.annotate(n=Count("books")).values("country", "n") возвращает строку на автора, сгруппированную и по первичному ключу. .filter(books__...) после values(...).annotate(n=Count("books")) использует тот же JOIN, что и агрегат, поэтому считаются только подходящие книги (см. Агрегаты и фильтры по связям «ко многим»). values_list() с flat=True сохраняет свою единственную колонку — вычисляемое значение всё равно группирует и фильтрует её: values_list("author__name", flat=True).annotate(n=Count("id")).filter(n__gt=3). Без выбранных полей (values()) вычисляемое значение добавляется ко всем полям.

aggregate()

Если каждая строка — строка исходного запроса (нет группировки, нет .distinct() по выбранным полям, нет среза, нет выбранного поля или сортировки через связь «ко многим»), это aggregate() самого запроса, и он может читать любое поле. Иначе он работает над строками, которые возвращает запрос, как над производной таблицей, и может читать только выбранные колонки — по выходному имени или по имени поля, которое выбирает переименованный ключ; всё остальное даёт QueryError, в том числе вычисляемое значение, которое не выбирает values_list() с flat=True. values("author_id").annotate(n=Count("id")).aggregate(most=Max("n")), values_list("rating", flat=True).distinct().aggregate(total=Sum("rating")), order_by("-rating")[:3].aggregate(...). Запрос, читающий оконную функцию, тоже агрегирует по производной таблице.

iterator()

Перебирает строки страницами. Без order_by() страницы идут по Meta.ordering, иначе по ключу группировки сгруппированного запроса, иначе по первичному ключу; добавляются колонки, делающие сортировку уникальной для каждой строки: ключ группировки у сгруппированного запроса, выбранные поля у запроса с .distinct(), иначе первичный ключ (если сортировка ещё не уникальна) и каждое выбранное поле через связь «ко многим». Постраничный вывод по курсору (безопасен при одновременных удалениях), если каждая колонка сортировки — выбранное поле самой модели, иначе через OFFSET. Собственные срез и after_cursor() запроса ограничивают весь перебор.

.distinct() с сортировкой по невыбранному полю

SELECT DISTINCT должен выбирать каждую колонку ORDER BY, поэтому колонка сортировки тоже выбирается: строки, различающиеся только ею, остаются разными, и одно выбранное значение может прийти несколько раз. В строках — только запрошенные поля:

# оценки 5 "sci", 4 "art", 3 "sci", 3 "art"
await Book.objects.all().order_by("-rating").distinct().values_list("subject", flat=True)  # ["sci", "art", "sci", "art"]

Чтобы получить каждое значение один раз, сортируйте только по выбранным полям или уберите сортировку через order_by().

Операции над множествами

union()/intersection()/difference() запросов .values()/.values_list() возвращают QuerySet объединённых строк. Каждая ветка выбирает одинаковое число колонок (иначе QueryError); строки получают вид (словари, кортежи, отдельные значения, именованные кортежи) и имена первой ветки, а каждая колонка раскодируется через первую ветку, которой известно её поле. Ветка может быть отсортирована, обрезана срезом или сама быть операцией над множествами (она объединяется как производная таблица); ветки могут принадлежать разным моделям. Цепочка вызовов объединяет каждую новую ветку со всем результатом, полученным до неё, как у запросов к моделям. Такое объединение поддерживает order_by() по выходным именам, limit()/offset()/срезы/индекс, count(), exists(), first()/last() (без сортировки — по всем выходным колонкам), get(), aggregate() (по выходным именам), iterator() (страницы через OFFSET, все выходные колонки задают постоянный порядок равных строк), stream(), sql()/explain() и работает как значение __in: Author.objects.filter(id__in=a.values_list("author_id", flat=True).union(b...)). filter()/exclude()/annotate()/update()/delete() на нём дают QueryError — применяйте их к веткам. .values(...)/.values_list(...) у объединения запросов к моделям применяют вызов к каждой ветке, сохраняя сортировку объединения (по полям, которые выбирают строки) и срез. Объединение запроса к модели с запросом значений даёт QueryError.

Связи и pk в путях к полям

Путь к полю может заканчиваться связью или pk везде, где принимается путь к полю, — в values()/values_list(), order_by(), group_by(), distinct(<fields>), F() и агрегатах:

  • имя прямой связи (values("author")) читает её собственную колонку ключа (author_id) без JOIN: значение остаётся, даже если связанную строку скрывает её запрос по умолчанию (арендатор, мягкое удаление, Meta.manager), как и сама колонка; only("author") загружает эту колонку;
  • связь «ко многим» (values("tags"), values("books")) или обратная «один-к-одному» читает первичный ключ связанной строки — строку на каждую связанную строку, NULL для строки без неё — через тот же JOIN, что и values("tags__id"), с учётом запроса связанной модели по умолчанию;
  • pk в конце пути (values("tags__pk"), values("author__pk"), order_by("tags__pk"), group_by("author__pk"), only("pk")) читает поле первичного ключа модели, до которой доходит путь, — через JOIN, поэтому author__pk равен NULL для скрытого автора, а author сохраняет значение колонки.

В результате остаётся переданное имя ({"tags": 1}, row.tags у строки с named=True). Ключ, прочитанный так, работает как любое выбранное поле: values("tags").annotate(n=Count("id")) группирует по тегу (строка на тег и одна группа NULL для строк без тега; ключ — первичный ключ, поэтому агрегат по другой связи «ко многим» рядом с ним верен), и он сочетается с .distinct(), order_by(), count(), aggregate(), iterator(), union() и подзапросами __in (Tag.objects.filter(id__in=Book.objects.filter(...).values("tags"))). Составной первичный ключ (или связь с ним) читается как кортеж частей — values("pk"), values_list("shelf", flat=True) дают (1, 2), а None, если все части NULL; group_by(), order_by() ("pk"/"-pk" и Meta.ordering тоже) и distinct(<fields>) используют все части. Переименованный (values(key="pk")), это тот же ключ под новым именем: filter(key=(1, 2)), filter(key__in=[...]), order_by("-key") и values("key") читают его части так же, как pk. Count("shelf") считает строки с ключом, Count("shelf", distinct=True) — различные ключи, никогда не считая NULL. F() такой связи даёт QueryError с перечислением её частей.

Части дат в путях полей

Путь поля может заканчиваться частью даты, времени или даты-времени — в values()/values_list(), order_by(), group_by() и distinct(<fields>):

await Book.objects.all().values("added_at__year")
await Book.objects.all().values_list("added_at__hour", "added_at__date")
await Book.objects.all().values(hour="added_at__hour")
await Book.objects.all().order_by("-added_at__month")
await Book.objects.all().order_by("added_at__date").distinct("added_at__date")  # только PostgreSQL - DISTINCT ON
await Book.objects.annotate(count=Count("id")).group_by("added_at__year").values("added_at__year", "count")
Часть Значение
year, iso_year, quarter, month, week, day целое число
week_day 1 = воскресенье … 7 = суббота
iso_week_day 1 = понедельник … 7 = воскресенье
hour, minute, second, microsecond целое число
date, time дата / время суток значения «дата и время»

У DatetimeField есть все части. У DateField — только календарные (year … day), у TimeField — только время суток (hour … microsecond); часть, которой у поля нет, даёт FieldError. Путь может сначала пройти через связи: book__added_at__year. Часть значения «дата и время» берётся в текущем поясе — Timezone.override(), иначе настроенном, — так же, как у операторов фильтра __year/__date; при use_timezone=True time приходит со смещением пояса, как значение TimeField.

distinct() принимает и имя вычисляемого значения: .annotate(year=ExtractYear("added_at")).distinct("year").order_by("year", "id"). В PostgreSQL пояс в Extract() пишется в SQL литералом, чтобы одна и та же часть в SELECT, ORDER BY и DISTINCT ON была одним выражением; в фильтре (added_at__hour=...) пояс по-прежнему передаётся параметром.