Справочник по API QuerySet


Этот документ описывает детали API QuerySet. Он основан на материале, представленном в руководствах модель и запросы к базе данных, так что вы, вероятно, захотите прочитать и понять эти документы, прежде чем читать этот.

Во всем этом справочнике мы будем использовать example blog models, представленные в database query guide.

Когда выполняются QuerySet
Внутренне QuerySet может быть создан, отфильтрован, нарезан и, как правило, передан без фактического запроса к базе данных. На самом деле никаких действий с базой данных не происходит, пока вы не сделаете что-то для оценки набора запросов.

Вы можете выполнить QuerySet следующими способами:

Итерация. QuerySet является итеративным, и он выполняет свой запрос к базе данных при первой итерации по нему. Например, это напечатает заголовок всех записей в базе данных:

for e in Entry.objects.all():
print(e.headline)
Примечание: не используйте это, если все, что вы хотите сделать, это определить, существует ли хотя бы один результат. Более эффективно использовать exists().

Срезы. Как объяснено в Ограничение QuerySet, QuerySet может быть нарезан, используя синтаксис Python для срезов массивов. Срез невычисленного QuerySet обычно возвращает другой невычисленный QuerySet, но Django выполнит запрос к базе данных, если вы используете параметр «step» синтаксиса среза, и вернет список. Срез QuerySet, который был вычислен, также возвращает список.

Также обратите внимание, что даже если срез невычисленного QuerySet возвращает другой невычисленный QuerySet, дальнейшее его изменение (например, добавление дополнительных фильтров или изменение порядка) недопустимо, поскольку это плохо переводится в SQL и не будет иметь четкого значения.

Pickling/Кэширование. См. следующий раздел для получения подробной информации о том, что происходит при pickling QuerySets. Для целей этого раздела важно, чтобы результаты считывались из базы данных.

repr(). QuerySet вычисляется, когда вы вызываете repr() для него. Это удобно для интерактивного интерпретатора Python, поэтому вы можете сразу увидеть свои результаты при интерактивном использовании API.

len(). QuerySet вычисляется, когда вы вызываете len() для него. Это, как вы могли ожидать, возвращает длину списка результатов.

Примечание. Если вам нужно только определить количество записей в наборе (и вам не нужны фактические объекты), гораздо эффективнее обрабатывать количество на уровне базы данных, используя SQL SELECT COUNT (*). Именно по этой причине Django предоставляет метод count().

list(). Принудительное вычисление QuerySet путем вызова list() для него. Например:

entry_list = list(Entry.objects.all())
bool(). Тестирование QuerySet в логическом контексте, например, с использованием bool(), or, and или оператора if, вызовет выполнение запроса. Если есть хотя бы один результат, QuerySet равен True, иначе False. Например:

if Entry.objects.filter(headline="Test"):
print("There is at least one Entry with the headline Test")
Примечание. Если вы хотите определить, существует ли хотя бы один результат (и вам не нужны реальные объекты), более эффективно использовать exists().

Pickling QuerySet
Если вы используете pickle для QuerySet, это загрузит все результаты в память. Pickling обычно используется в качестве предвестника кеширования, и когда кешированный набор запросов перезагружается, вы хотите, чтобы результаты уже присутствовали и были готовы к использованию (чтение из базы данных может занять некоторое время, что отрицательно сказывается на цели кеширования). Это означает, что когда вы unpickle QuerySet, он содержит результаты в тот момент, когда он был выбран, а не результаты, которые в данный момент находятся в базе данных.

Если вы хотите pickle только необходимую информацию для воссоздания QuerySet из базы данных позднее, выберите атрибут query для QuerySet. Затем вы можете воссоздать исходный QuerySet (без каких-либо результатов), используя код, подобный следующему:

>>> import pickle
>>> query = pickle.loads(s) # Assuming 's' is the pickled string.
>>> qs = MyModel.objects.all()
>>> qs.query = query # Restore the original 'query'.
Атрибут query является непрозрачным объектом. Он представляет собой внутреннюю часть конструкции запроса и не является частью общедоступного API. Тем не менее, безопасно (и полностью поддерживается) pickle и unpickle содержимое атрибута, как описано здесь.

Ограничения на QuerySet.values_list()

Если вы воссоздадите QuerySet.values_list() с использованием упакованного атрибута query, он будет преобразован в QuerySet.values():

>>> import pickle
>>> qs = Blog.objects.values_list('id', 'name')
>>> qs
<QuerySet [(1, 'Beatles Blog')]>
>>> reloaded_qs = Blog.objects.all()
>>> reloaded_qs.query = pickle.loads(pickle.dumps(qs.query))
>>> reloaded_qs
<QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>
Вы не можете использовать pickle между версиями

Соленья `` QuerySets`` действительны только для той версии Django, которая использовалась для их генерации. Если вы генерируете рассол с использованием Django версии N, нет никакой гарантии, что рассол будет читаем с Django версии N + 1. Соленые огурцы не должны использоваться как часть долгосрочной архивной стратегии.

Так как ошибки совместимости с помощью pickle могут быть трудно диагностируемыми, например, незаметно поврежденные объекты, возникает RuntimeWarning, когда вы пытаетесь открепить набор запросов в версии Django, отличной от той, в которой он был выбран.

QuerySet API
Вот формальное объявление QuerySet:

class QuerySet(model=None, query=None, using=None, hints=None)[исходный код]
Обычно работа с QuerySet состоит в использовании цепочек фильтров. Для этого большинство методов QuerySet возвращает новый «queryset». Эти методы подробно описаны ниже в этом разделе.

Класс QuerySet имеет два открытых атрибута, которые вы можете использовать для самоанализа:

ordered
True, если QuerySet упорядочен - т.е. имеет order_by() или сортировку по умолчанию для модели, иначе False.

db
База данных, которая будет использоваться, если этот запрос выполняется сейчас.

Примечание

Параметр query для QuerySet существует для того, чтобы специализированные подклассы запросов могли реконструировать внутреннее состояние запроса. Значение параметра является непрозрачным представлением этого состояния запроса и не является частью общедоступного API.

Методы, которые возвращают новый QuerySet
Django предоставляет ряд методов уточнения QuerySet, которые изменяют либо типы результатов, возвращаемых QuerySet, либо способ выполнения его SQL-запроса.

filter()
filter(*args, **kwargs)
Возвращает новый QuerySet, содержащий объекты, которые соответствуют заданным параметрам поиска.

Параметры поиска (**kwargs) должны быть в формате, описанном в Полевые поиски ниже. Несколько параметров объединяются через `` И`` в базовом операторе SQL.

Если вам необходимо выполнить более сложные запросы (например, запросы с операторами OR), вы можете использовать Q objects (*args).

exclude()
exclude(*args, **kwargs)
Возвращает новый QuerySet, содержащий объекты, которые не соответствуют указанным параметрам поиска.

Параметры поиска (**kwargs) должны быть в формате, описанном в Поиск по полям ниже. Несколько параметров объединяются с помощью AND в базовом операторе SQL, и все это заключено в ``NOT() ``.

Этот пример исключает все записи, чье pub_date позже, чем 2005-1-3 И чьи headline равно «Hello»:

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')
В терминах SQL это оценивается как:

SELECT ...
WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')
В этом примере исключаются все записи, чье ``pub_date` позже, чем 2005-1-3 ИЛИ с заголовком «Hello»:

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline='Hello')
В терминах SQL это оценивается как:

SELECT ...
WHERE NOT pub_date > '2005-1-3'
AND NOT headline = 'Hello'
Обратите внимание, что второй пример является более ограничительным.

Если вам необходимо выполнить более сложные запросы (например, запросы с операторами OR), вы можете использовать Q objects (*args).

annotate()
annotate(*args, **kwargs)
Аннотирует каждый объект в QuerySet с помощью предоставленного списка выражений запроса. Выражение может быть простым значением, ссылкой на поле в модели (или любых связанных моделях) или агрегированным выражением (средние значения, суммы и т.д.), которое было вычислено для объектов, связанных с объектами в QuerySet.

Каждый аргумент annotate() является аннотацией, которая будет добавлена к каждому объекту в QuerySet, который возвращается.

Функции агрегации, предоставляемые Django, описаны в Функции агрегации ниже.

Аннотации, указанные с помощью ключевых аргументов, будут использовать ключевое слово в качестве псевдонима для аннотации. Для анонимных аргументов будет создан псевдоним, основанный на имени агрегатной функции и агрегируемого поля модели. Только агрегатные выражения, которые ссылаются на одно поле, могут быть анонимными аргументами. Все остальное должно быть ключевым аргументом.

Например, если вы манипулировали списком блогов, вы можете определить, сколько записей было сделано в каждом блоге:

>>> from django.db.models import Count
>>> q = Blog.objects.annotate(Count('entry'))
# The name of the first blog
>>> q[0].name
'Blogasaurus'
# The number of entries on the first blog
>>> q[0].entry__count
42
Модель Blog не определяет атрибут entry__count сама по себе, но с помощью ключевого аргумента для указания агрегатной функции вы можете контролировать имя аннотации:

>>> q = Blog.objects.annotate(number_of_entries=Count('entry'))
# The number of entries on the first blog, using the name provided
>>> q[0].number_of_entries
42
Подробное обсуждение агрегации смотрите в руководство по агрегации.

alias()
alias(*args, **kwargs)
New in Django 3.2.
То же, что annotate(), но вместо аннотирования объектов в QuerySet сохраняет выражение для последующего повторного использования с другими методами QuerySet. Это полезно, когда результат самого выражения не нужен, но он используется для фильтрации, упорядочивания или как часть сложного выражения. Если не выбрать неиспользуемое значение, из базы данных удаляется избыточная работа, что должно привести к повышению производительности.

Например, если вы хотите найти блоги с более чем 5 записями, но не интересуетесь точным количеством записей, вы можете сделать это:

>>> from django.db.models import Count
>>> blogs = Blog.objects.alias(entries=Count('entry')).filter(entries__gt=5)
alias() можно использовать вместе с annotate(), exclude(), filter(), order_by() и update(). Чтобы использовать выражение с псевдонимом с другими методами (например aggregate()), вы должны преобразовать его в аннотацию:

Blog.objects.alias(entries=Count('entry')).annotate(
entries=F('entries'),
).aggregate(Sum('entries'))
filter() и order_by() могут принимать выражения напрямую, но построение и использование выражений часто не происходит в одном и том же месте (например, метод QuerySet создает выражения для последующего использования в представлениях). alias() позволяет создавать сложные выражения постепенно, возможно, охватывая несколько методов и модулей, ссылаться на части выражения по их псевдонимам и использовать только annotate() для окончательного результата.

order_by()
order_by(*fields)
По умолчанию результаты, возвращаемые QuerySet, упорядочиваются с помощью кортежа, заданного параметром ordering в классе Meta модели. Вы можете переопределить это для каждого QuerySet, используя метод order_by.

Пример:

Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')
Приведенный выше результат будет упорядочен по убыванию pub_date, затем по возрастанию headline. Отрицательный знак перед "-pub_date" указывает нисходящий порядок. Восходящий порядок подразумевается. Чтобы упорядочить случайным образом, используйте "?", например так:

Entry.objects.order_by('?')
Примечание: запросы order_by('?') могут быть дорогими и медленными, в зависимости от используемой вами базы данных.

Чтобы упорядочить по полю в другой модели, используйте тот же синтаксис, что и при запросах к отношениям модели. То есть имя поля, за которым следует двойное подчеркивание (__), за которым следует имя поля в новой модели и т.д. для всех моделей, к которым вы хотите присоединиться. Например:

Entry.objects.order_by('blog__name', 'headline')
Если вы попытаетесь упорядочить по полю, которое относится к другой модели, Django будет использовать упорядочение по умолчанию для связанной модели или упорядочить по первичному ключу связанной модели, если его нет :attr: Meta.ordering <django.db .models.Options.ordering> `указано. Например, поскольку модель ` Blog`` не имеет установленного порядка по умолчанию:

Entry.objects.order_by('blog')
…идентично:

Entry.objects.order_by('blog__id')
Если бы Blog имел ordering = ['name'], то первый набор запросов был бы идентичен:

Entry.objects.order_by('blog__name')
Вы также можете упорядочить выражения запроса, вызвав asc() или desc() в выражении:

Entry.objects.order_by(Coalesce('summary', 'headline').desc())
asc() и desc() имеют аргументы (nulls_first и nulls_last), которые управляют сортировкой нулевых значений.

Будьте осторожны при упорядочении по полям в связанных моделях, если вы также используете different(). Смотрите примечания в different() для объяснения того, как упорядочение связанной модели может изменить ожидаемые результаты.

Примечание

Допустимо указывать многозначное поле для упорядочения результатов по (например, полю a ManyToManyField или обратному отношению ForeignKey).

Рассмотрим этот случай:

class Event(Model):
parent = models.ForeignKey(
'self',
on_delete=models.CASCADE,
related_name='children',
)
date = models.DateField()

Event.objects.order_by('children__date')
Здесь потенциально могут быть данные о множественном упорядочении для каждого события; каждый Event с несколькими children будет возвращаться несколько раз в новый QuerySet, который создает order_by(). Другими словами, использование order_by() в QuerySet может вернуть больше элементов, чем вы работали с самого начала - что, вероятно, ни ожидаемо, ни полезно.

Таким образом, будьте осторожны при использовании многозначного поля для упорядочения результатов. Если вы можете быть уверены, что для каждого из заказываемых вами товаров будет только один фрагмент данных для заказа, такой подход не должен вызывать проблем. Если нет, убедитесь, что результаты соответствуют вашим ожиданиям.

Нет никакого способа указать, должен ли порядок быть чувствительным к регистру. Что касается чувствительности к регистру, Django будет упорядочивать результаты, однако ваш сервер базы данных обычно упорядочивает их.

Вы можете упорядочить по полю, преобразованному в нижний регистр, с помощью Lower, что обеспечит последовательное упорядочение:

Entry.objects.order_by(Lower('headline').desc())
Если вы не хотите, чтобы к запросу применялся какой-либо порядок, даже порядок по умолчанию, вызовите order_by() без параметров.

Вы можете узнать, упорядочен запрос или нет, проверив атрибут QuerySet.ordered, который будет True, если QuerySet был упорядочен каким-либо образом.

Каждый вызов order_by() очищает любую предыдущую сортировку. Например, этот запрос будет упорядочен pub_date, а не headline:

Entry.objects.order_by('headline').order_by('pub_date')
Предупреждение

Сортировка не является легкой операцией. Каждое поле, которое вы добавляете к сортировке, больше нагружает вашу базу данных. Каждый добавляемый вами внешний ключ будет также неявно включать все свои сортировки по умолчанию.

Если в запросе не указана сортировка, результаты возвращаются из базы данных в неуказанном порядке. Конкретное упорядочение гарантируется только при упорядочении с помощью набора полей, которые однозначно идентифицируют каждый объект в результатах. Например, если поле name не уникально, сортировка по нему не гарантирует, что объекты с одинаковыми именами всегда будут появляться в одном и том же порядке.

reverse()
reverse()
Используйте метод reverse() для изменения порядка, в котором возвращаются элементы набора запросов. Вызов reverse() во второй раз восстанавливает порядок в нормальном направлении.

Чтобы получить «последние» пять элементов в наборе запросов, вы можете сделать так:

my_queryset.reverse()[:5]
Обратите внимание, что это не совсем то же самое, что вырезание из конца последовательности в Python. В приведенном выше примере сначала будет возвращен последний элемент, затем предпоследний элемент и т.д. Если бы у нас была последовательность Python и мы посмотрели на seq[-5:], мы бы увидели пятый (последний) элемент первым. Django не поддерживает этот режим доступа (нарезка с конца), потому что это невозможно сделать эффективно в SQL.

Кроме того, обратите внимание, что reverse() обычно следует вызывать только для QuerySet, который имеет определенную сортировку (например, при запросе к модели, которая определяет сортировку по умолчанию, или при использовании: meth: order_by () `). Если такая сортировка не определена для данного ``QuerySet`, то вызов reverse() для него не имеет реального эффекта (сортировка не определена до вызова reverse() и останется неопределенной после этого).

distinct()
distinct(*fields)
Возвращает новый QuerySet, который использует SELECT DISTINCT в своем SQL-запросе. Это исключает повторяющиеся строки из результатов запроса.

По умолчанию QuerySet не удаляет повторяющиеся строки. На практике это редко является проблемой, потому что простые запросы, такие как Blog.objects.all(), не вводят возможность дублирования строк результатов. Однако, если ваш запрос охватывает несколько таблиц, можно получить дублированные результаты при вычислении QuerySet. В этом случае вы будете использовать distinct().

Примечание

Любые поля, используемые в вызове order_by(), включены в столбцы SQL SELECT. Иногда это может привести к неожиданным результатам при использовании в сочетании с distinct(). Если вы упорядочиваете по полям из связанной модели, эти поля будут добавлены в выбранные столбцы, и в противном случае дублирующие строки могут оказаться разными. Поскольку дополнительные столбцы не отображаются в возвращаемых результатах (они предназначены только для поддержки сортировки), иногда кажется, что возвращаются нечеткие результаты.

Аналогично, если вы используете запрос values() для ограничения выбранных столбцов, столбцы, используемые в любом из order_by() (или сортировка мо дели по умолчанию), все равно будут задействованы и могут повлиять на уникальность результатов.

Мораль здесь заключается в том, что если вы используете distinct(), будьте осторожны с сортировкой по связанным моделям. Аналогично, при совместном использовании distinct() и values() будьте осторожны при сортировке по полям, не входящим в вызов values().

Только в PostgreSQL вы можете передавать позиционные аргументы (*fields), чтобы указать имена полей, к которым должен применяться DISTINCT. Это преобразуется в SQL-запрос SELECT DISTINCT ON. Вот в чем разница. Для обычного вызова distinct() база данных сравнивает каждое поле в каждой строке, определяя, какие строки различны. Для вызова distinct() с указанными именами полей, база данных будет сравнивать только указанные имена полей.

Примечание

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

Например, SELECT DISTINCT ON (a) дает вам первую строку для каждого значения в столбце a. Если вы не укажете сортировку, вы получите произвольную строку.

Примеры (те, которые после первого будут работать только на PostgreSQL):

>>> Author.objects.distinct()
[...]

>>> Entry.objects.order_by('pub_date').distinct('pub_date')
[...]

>>> Entry.objects.order_by('blog').distinct('blog')
[...]

>>> Entry.objects.order_by('author', 'pub_date').distinct('author', 'pub_date')
[...]

>>> Entry.objects.order_by('blog__name', 'mod_date').distinct('blog__name', 'mod_date')
[...]

>>> Entry.objects.order_by('author', 'pub_date').distinct('author')
[...]
Примечание

Имейте в виду, что order_by() использует любую сортировку по умолчанию, связанную с моделью, которая была определена. Возможно, вам придется явно сортировать по отношению _id или по указанному полю, чтобы убедиться, что выражения DISTINCT ON совпадают с выражениями в начале ORDER BY. Например, если модель Blog определила ordering с помощью name:

Entry.objects.order_by('blog').distinct('blog')
… не сработает, потому что запрос будет отсортирован по blog__name, что не соответствует выражению DISTINCT ON. Вам нужно явно упорядочить по полю отношения _id (в данном случае blog_id) или по ссылке (blog__pk), чтобы убедиться, что оба выражения совпадают.

values()
values(*fields, **expressions)
Возвращает QuerySet, который возвращает словари, а не экземпляры модели, когда используется как итеративный.

Каждый из этих словарей представляет объект с ключами, соответствующими именам атрибутов объектов модели.

В этом примере сравниваются словари values() с объектами нормальной модели:

# This list contains a Blog object.
>>> Blog.objects.filter(name__startswith='Beatles')
<QuerySet [<Blog: Beatles Blog>]>

# This list contains a dictionary.
>>> Blog.objects.filter(name__startswith='Beatles').values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
Метод values() принимает необязательные позиционные аргументы, *fields, которые определяют имена полей, которыми должен ограничиваться SELECT. Если вы укажете поля, каждый словарь будет содержать только ключи/значения полей для указанных вами полей. Если вы не укажете поля, каждый словарь будет содержать ключ и значение для каждого поля в таблице базы данных.

Пример:

>>> Blog.objects.values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
>>> Blog.objects.values('id', 'name')
<QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>
Метод values() также принимает необязательные именованные аргументы, **expressions, которые передаются через annotate():

>>> from django.db.models.functions import Lower
>>> Blog.objects.values(lower_name=Lower('name'))
<QuerySet [{'lower_name': 'beatles blog'}]>
Вы можете использовать встроенные и собственные средства поиска при сортировке. Например:

>>> from django.db.models import CharField
>>> from django.db.models.functions import Lower
>>> CharField.register_lookup(Lower)
>>> Blog.objects.values('name__lower')
<QuerySet [{'name__lower': 'beatles blog'}]>
Аггрегирование с values() применяется перед другими аргументами в том же выражении values(). Если вам нужно сгруппировать по другому значению, добавьте его в более раннее выражение values(). Например:

>>> from django.db.models import Count
>>> Blog.objects.values('entry__authors', entries=Count('entry'))
<QuerySet [{'entry__authors': 1, 'entries': 20}, {'entry__authors': 1, 'entries': 13}]>
>>> Blog.objects.values('entry__authors').annotate(entries=Count('entry'))
<QuerySet [{'entry__authors': 1, 'entries': 33}]>
Несколько тонкостей, о которых стоит упомянуть:

Если у вас есть поле с именем foo, которое является ForeignKey, вызов values() по умолчанию вернет ключ словаря с именем foo_id, поскольку это имя атрибута скрытой модели, в котором хранится фактическое значение (атрибут foo относится к связанной модели). Когда вы вызываете values() и передаете имена полей, вы можете передать либо foo, либо foo_id, и вы получите то же самое (ключ словаря будет соответствовать имени поля, которые вы передали).

Например:

>>> Entry.objects.values()
<QuerySet [{'blog_id': 1, 'headline': 'First Entry', ...}, ...]>

>>> Entry.objects.values('blog')
<QuerySet [{'blog': 1}, ...]>

>>> Entry.objects.values('blog_id')
<QuerySet [{'blog_id': 1}, ...]>
При использовании values() вместе с distinct() помните, что сортировка может повлиять на результаты. Смотрите примечание в distinct() для деталей.

Если вы используете выражение values() после вызова extra(), любые поля, определенные аргументом select в extra(), должны быть явно включены в вызов values(). При любом вызов extra() после вызова values() лишние выбранные поля будут игнорироваться.

Вызов only() и defer() после values() не имеет смысла, поэтому это вызовет ошибку TypeError.

Объединение преобразований и агрегирования требует использования двух вызовов annotate(), либо явно, либо в качестве именованных аргументов для values(). Как и выше, если преобразование зарегистрировано в соответствующем типе поля, первое annotate() может быть опущено, поэтому следующие примеры эквивалентны:

>>> from django.db.models import CharField, Count
>>> from django.db.models.functions import Lower
>>> CharField.register_lookup(Lower)
>>> Blog.objects.values('entry__authors__name__lower').annotate(entries=Count('entry'))
<QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]>
>>> Blog.objects.values(
... entry__authors__name__lower=Lower('entry__authors__name')
... ).annotate(entries=Count('entry'))
<QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]>
>>> Blog.objects.annotate(
... entry__authors__name__lower=Lower('entry__authors__name')
... ).values('entry__authors__name__lower').annotate(entries=Count('entry'))
<QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]>
Это полезно, когда вы знаете, что вам понадобятся значения только из небольшого числа доступных полей, и вам не понадобятся функциональные возможности объекта экземпляра модели. Более эффективно выбирать только те поля, которые вам нужны.

Наконец, обратите внимание, что вы можете вызывать filter(), order_by() и т.д. после вызова values(), это означает, что эти два вызова идентичны:

Blog.objects.values().order_by('id')
Blog.objects.order_by('id').values()
Люди, которые сделали Django, предпочитают сначала поместить все методы, влияющие на SQL, а затем (необязательно) любые методы, влияющие на вывод (такие как values()), но это не имеет большого значения. Это ваш шанс по-настоящему выставить напоказ свой индивидуализм.

Вы также можете ссылаться на поля в связанных моделях с обратными связями через атрибуты OneToOneField, ForeignKey и ManyToManyField:

>>> Blog.objects.values('name', 'entry__headline')
<QuerySet [{'name': 'My blog', 'entry__headline': 'An entry'},
{'name': 'My blog', 'entry__headline': 'Another entry'}, ...]>
Предупреждение

Потому что :class:` ~ django.db.models.ManyToManyField` атрибуты и обратные отношения могут иметь несколько связанных строк, в том числе они могут повлиять на размер набора результатов. Это будет особенно заметно, если вы включите несколько таких полей в ваш запрос values(), и в этом случае будут возвращены все возможные комбинации.

values_list()
values_list(*fields, flat=False, named=False)
Это похоже на values(), за исключением того, что вместо возврата словарей он возвращает кортежи при повторении. Каждый кортеж содержит значение из соответствующего поля или выражения, переданное в вызов values_list() - поэтому первый элемент является первым полем и т.д. Например:

>>> Entry.objects.values_list('id', 'headline')
<QuerySet [(1, 'First entry'), ...]>
>>> from django.db.models.functions import Lower
>>> Entry.objects.values_list('id', Lower('headline'))
<QuerySet [(1, 'first entry'), ...]>
Если вы передаете только одно поле, вы также можете передать параметр flat. Если True, это будет означать, что возвращаемые результаты будут единичными значениями, а не кортежем. Пример должен прояснить разницу:

>>> Entry.objects.values_list('id').order_by('id')
<QuerySet[(1,), (2,), (3,), ...]>

>>> Entry.objects.values_list('id', flat=True).order_by('id')
<QuerySet [1, 2, 3, ...]>
Ошибочно передавать в flat, когда имеется более одного поля.

Вы можете передать `` named = True`` для получения результатов в виде: func: ~ python: collection.namedtuple

>>> Entry.objects.values_list('id', 'headline', named=True)
<QuerySet [Row(id=1, headline='First entry'), ...]>
Использование именованного кортежа может сделать результаты более читабельными за счет небольшого снижения производительности за преобразование результатов в именованный кортеж.

Если вы не передадите никаких значений в values_list(), он вернет все поля в модели в том порядке, в котором они были объявлены.

Общей необходимостью является получение определенного значения поля определенного экземпляра модели. Для этого используйте values_list(), а затем get():

>>> Entry.objects.values_list('headline', flat=True).get(pk=1)
'First entry'
values() и values_list() предназначены для оптимизации для конкретного случая использования: извлечение подмножества данных без затрат на создание экземпляра модели. Эта метафора разваливается при работе со многими ко многим и другими многозначными отношениями (такими как отношение «один ко многим» обратного внешнего ключа), потому что предположение «одна строка, один объект» не выполняется.

Например, обратите внимание на поведение при запросе через ManyToManyField:

>>> Author.objects.values_list('name', 'entry__headline')
<QuerySet [('Noam Chomsky', 'Impressions of Gaza'),
('George Orwell', 'Why Socialists Do Not Believe in Fun'),
('George Orwell', 'In Defence of English Cooking'),
('Don Quixote', None)]>
Авторы с несколькими записями появляются несколько раз, а авторы без записей имеют None для заголовка записи.

Аналогично, при запросе обратного внешнего ключа, None появляется для записей, не имеющих автора:

>>> Entry.objects.values_list('authors')
<QuerySet [('Noam Chomsky',), ('George Orwell',), (None,)]>
dates()
dates(field, kind, order='ASC')
Возвращает QuerySet, который вычисляет список объектов datetime.date, представляющих все доступные даты определенного вида в QuerySet.

field должно быть именем DateField вашей модели. kind должен быть либо "year", "month", "week", либо "day". Каждый объект datetime.date в списке результатов «усекается» до заданного type.

"year" возвращает список всех различных значений года.
"month" возвращает список всех различных значений года/месяца.
"week" возвращает список всех различных значений года/недели. Все даты будут понедельником.
"day" возвращает список всех различных значений года/месяца/дня.
order, который по умолчанию равен 'ASC', должен быть либо 'ASC', либо 'DESC'. Это указывает, как сортировать результаты.

Примеры:

>>> Entry.objects.dates('pub_date', 'year')
[datetime.date(2005, 1, 1)]
>>> Entry.objects.dates('pub_date', 'month')
[datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)]
>>> Entry.objects.dates('pub_date', 'week')
[datetime.date(2005, 2, 14), datetime.date(2005, 3, 14)]
>>> Entry.objects.dates('pub_date', 'day')
[datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)]
>>> Entry.objects.dates('pub_date', 'day', order='DESC')
[datetime.date(2005, 3, 20), datetime.date(2005, 2, 20)]
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
[datetime.date(2005, 3, 20)]
datetimes()
datetimes(field_name, kind, order='ASC', tzinfo=None, is_dst=None)
Возвращает QuerySet, который оценивает список объектов datetime.datetime, представляющих все доступные даты определенного вида в содержимом QuerySet.

field_name` должно быть именем ``DateTimeField вашей модели.

kind должен быть одним из: "year", "month", "week", "day", "hour", "minute", "second". Каждый объект datetime.datetime в списке результатов «усекается» до заданного type.

order, который по умолчанию равен 'ASC', должен быть либо 'ASC', либо 'DESC'. Это указывает, как сортировать результаты.

tzinfo определяет часовой пояс, в который конвертируются даты и время перед усечением. Действительно, данная дата и время имеют разные представления в зависимости от используемого часового пояса. Этот параметр должен быть объектом datetime.tzinfo. Если это None, Django использует текущий часовой пояс. Это не действует, если USE_TZ равно False.

is_dst указывает, должен ли pytz интерпретировать несуществующие и неоднозначные даты в летнее время. По умолчанию (когда is_dst=None), pytz выдает исключение для таких datetime.

Не рекомендуется, начиная с версии 4.0: Параметр is_dst является устаревшим и будет удален в Django 5.0.

Примечание

Эта функция выполняет преобразование часового пояса непосредственно в базе данных. Как следствие, ваша база данных должна иметь возможность интерпретировать значение tzinfo.tzname(None). Это приводит к следующим требованиям:

SQLite: нет требований. Преобразования выполняются на языке Python.
PostgreSQL: никаких требований (см. Часовые пояса <time).
Oracle: нет требований (см. Выбор файла часового пояса).
MySQL: загрузить таблицы часовых поясов с помощью mysql_tzinfo_to_sql.
none()
none()
Вызов none() создаст набор запросов, который никогда не возвращает никаких объектов, и никакой запрос не будет выполняться при доступе к результатам. Набор запросов qs.none() является экземпляром EmptyQuerySet.

Примеры:

>>> Entry.objects.none()
<QuerySet []>
>>> from django.db.models.query import EmptyQuerySet
>>> isinstance(Entry.objects.none(), EmptyQuerySet)
True
all()
all()
Возвращает копию текущего QuerySet (или подкласса QuerySet). Это может быть полезно в ситуациях, когда вы захотите передать либо менеджер модели, либо QuerySet и выполнить дальнейшую фильтрацию по результату. После вызова all() для любого объекта у вас обязательно будет QuerySet для работы.

Когда QuerySet вычисляется, он обычно кэширует свои результаты. Если данные в базе данных могли измениться после вычисления QuerySet, вы можете получить обновленные результаты для того же запроса, вызвав all() для ранее вычисленного QuerySet.

union()
union(*other_qs, all=False)
Использует оператор SQL UNION для объединения результатов двух или более QuerySet’ов. Например:

>>> qs1.union(qs2, qs3)
Оператор UNION выбирает только отдельные значения по умолчанию. Чтобы разрешить повторяющиеся значения, используйте аргумент all=True.

union(), intersection() и difference() возвращают экземпляры модели с типом первого QuerySet, даже если аргументами являются QuerySet других моделей. Передача разных моделей работает до тех пор, пока список SELECT одинаков во всех QuerySet (по крайней мере, типы, имена не имеют значения, если типы находятся в одном порядке). В таких случаях вы должны использовать имена столбцов из первого QuerySet в методах QuerySet, примененных к результирующему QuerySet. Например:

>>> qs1 = Author.objects.values_list('name')
>>> qs2 = Entry.objects.values_list('headline')
>>> qs1.union(qs2).order_by('name')
Кроме того, только LIMIT, OFFSET, COUNT(*), ORDER BY и указание столбцов (т.е. срезов, count(), exists(), order_by() и values()/values_list()) разрешены в результирующем QuerySet. Кроме того, базы данных накладывают ограничения на то, какие операции разрешены в комбинированных запросах. Например, большинство баз данных не позволяют использовать LIMIT или OFFSET в комбинированных запросах.

intersection()
intersection(*other_qs)
Использует оператор SQL INTERSECT для возврата общих элементов двух или более QuerySet’ов. Например:

>>> qs1.intersection(qs2, qs3)
Смотрите union() для некоторых ограничений.

difference()
difference(*other_qs)
Использует оператор SQL EXCEPT для хранения только элементов, присутствующих в QuerySet, но не в каких-либо других QuerySet’ах. Например:

>>> qs1.difference(qs2, qs3)
Смотрите union() для некоторых ограничений.

select_related()
select_related(*fields)
Возвращает QuerySet, который будет «следовать» отношениям внешнего ключа, выбирая дополнительные данные связанного объекта при выполнении своего запроса. Это повышение производительности, которое приводит к одному более сложному запросу, но означает, что дальнейшее использование отношений внешнего ключа не потребует запросов к базе данных.

Следующие примеры иллюстрируют разницу между простыми поисками и с использованием select_related(). Вот стандартный поиск:

# Hits the database.
e = Entry.objects.get(id=5)

# Hits the database again to get the related Blog object.
b = e.blog
И вот с select_related:

# Hits the database.
e = Entry.objects.select_related('blog').get(id=5)

# Doesn't hit the database, because e.blog has been prepopulated
# in the previous query.
b = e.blog
Вы можете использовать select_related() с любым набором запросов объектов:

from django.utils import timezone

# Find all the blogs with entries scheduled to be published in the future.
blogs = set()

for e in Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog'):
# Without select_related(), this would make a database query for each
# loop iteration in order to fetch the related blog for each entry.
blogs.add(e.blog)
Порядок цепочек filter() и select_related() не важен. Эти наборы запросов эквивалентны:

Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog')
Entry.objects.select_related('blog').filter(pub_date__gt=timezone.now())
Вы можете следить за внешними ключами так же, как запрашивать их. Если у вас есть следующие модели:

from django.db import models

class City(models.Model):
# ...
pass

class Person(models.Model):
# ...
hometown = models.ForeignKey(
City,
on_delete=models.SET_NULL,
blank=True,
null=True,
)

class Book(models.Model):
# ...
author = models.ForeignKey(Person, on_delete=models.CASCADE)
…тогда вызов Book.objects.select_related('author__hometown').get(id=4) кеширует связанный Person и связанный City:

# Hits the database with joins to the author and hometown tables.
b = Book.objects.select_related('author__hometown').get(id=4)
p = b.author # Doesn't hit the database.
c = p.hometown # Doesn't hit the database.

# Without select_related()...
b = Book.objects.get(id=4) # Hits the database.
p = b.author # Hits the database.
c = p.hometown # Hits the database.
Вы можете ссылаться на любое отношение ForeignKey или OneToOneField в списке полей, передаваемых в select_related().

Вы также можете обратиться к обратному направлению OneToOneField в списке полей, переданных select_related - то есть вы можете пройти в OneToOneField и вернуться к объекту, для которого определено поле. Вместо указания имени поля используйте :attr:related_name<django.db.models.ForeignKey.related_name> `для поля в связанном объекте.

Могут быть ситуации, когда вы хотите вызвать select_related() с большим количеством связанных объектов, или когда вы не знаете всех отношений. В этих случаях можно вызывать select_related() без аргументов. Он будет следовать за всеми ненулевыми внешними ключами, которые он может найти - должны быть указаны внешние ключи, которые могут иметь значение null. Это не рекомендуется в большинстве случаев, так как это может сделать базовый запрос более сложным и вернуть больше данных, чем фактически необходимо.

Если вам нужно очистить список связанных полей, добавленных прошлыми вызовами select_related в QuerySet, вы можете передать None в качестве параметра:

>>> without_relations = queryset.select_related(None)
Цепочка вызовов select_related работает аналогично другим методам - то есть select_related('foo', 'bar') эквивалентна select_related('foo').select_related('bar').

prefetch_related()
prefetch_related(*lookups)
Возвращает QuerySet, который автоматически извлекает в одном пакете связанные объекты для каждого из указанных поисков.

Это имеет цель, аналогичную select_related, в том смысле, что оба предназначены для остановки потока запросов к базе данных, вызванного доступом к связанным объектам, но стратегия совершенно иная.

select_related работает путем создания соединения (join) SQL и включения полей связанного объекта в оператор SELECT. По этой причине select_related получает связанные объекты в одном запросе к базе данных. Тем не менее, чтобы избежать гораздо большего результирующего набора, который мог бы возникнуть в результате объединения через отношение „many“, select_related ограничен однозначными отношениями - внешним ключом и один-к-одному.

prefetch_related, с другой стороны, выполняет отдельный поиск для каждого отношения и выполняет «соединение» в Python. Это позволяет ему предварительно выбирать объекты «многие ко многим» и «многие к одному», что нельзя сделать с помощью select_related, в дополнение к внешнему ключу и отношениям «один к одному», которые поддерживаются select_related. Он также поддерживает предварительную выборку GenericRelation и GenericForeignKey, однако он должен быть ограничен однородным набором результатов. Например, предварительная выборка объектов, на которые ссылается GenericForeignKey, поддерживается только в том случае, если запрос ограничен одним ContentType.

Например, предположим, у вас есть эти модели:

from django.db import models

class Topping(models.Model):
name = models.CharField(max_length=30)

class Pizza(models.Model):
name = models.CharField(max_length=50)
toppings = models.ManyToManyField(Topping)

def __str__(self):
return "%s (%s)" % (
self.name,
", ".join(topping.name for topping in self.toppings.all()),
)
и запустите:

>>> Pizza.objects.all()
["Hawaiian (ham, pineapple)", "Seafood (prawns, smoked salmon)"...
Проблема в том, что каждый раз, когда Pizza.__str__() запрашивает self.toppings.all(), он должен запросить базу данных, поэтому Pizza.objects.all() выполнит запрос к таблице Toppings для каждого элемента в Pizza QuerySet.

Мы можем сократить до двух запросов, используя prefetch_related:

>>> Pizza.objects.all().prefetch_related('toppings')
Это подразумевает self.toppings.all() для каждого Pizza; теперь каждый раз, когда вызывается self.toppings.all(), вместо того, чтобы обращаться к базе данных за элементами, он будет находить их в предварительно выбранном кэше QuerySet, который был заполнен в одном запросе.

Таким образом, все соответствующие начинки будут извлечены в одном запросе и использованы для создания QuerySets, которые имеют предварительно заполненный кеш соответствующих результатов; эти QuerySets затем используются в вызовах self.toppings.all().

Дополнительные запросы в prefetch_related() выполняются после того, как QuerySet начал оцениваться и был выполнен п