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=...) пояс по-прежнему передаётся параметром.