Spec-Zone.ru › Django 4.0

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

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

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

Когда QuerySet оцениваются

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

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

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

    for e in Entry.objects.all():
        print(e.headline)
    

    Примечание: Не используйте этот способ, если вам нужно только определить, существует ли хотя бы один результат. Более эффективным является использование exists().

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

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

  • Архивирование/Кэширование. Подробности о том, что происходит при архивировании QuerySet, см. в следующем разделе. Важно для целей этого раздела то, что результаты считываются из базы данных.
  • 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().

Архивирование QuerySet

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

Если вы хотите сохранить только необходимую информацию для повторного создания 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. Однако безопасно (и полностью поддерживается) архивировать и распаковывать содержимое атрибута, как описано здесь.

Ограничения на 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'}]>

Вы не можете использовать архивы между версиями

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

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

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) должны иметь формат, описанный в Поиск по полям ниже. Несколько параметров объединяются через AND в базовом SQL-запросе.

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

exclude()

exclude(*args, **kwargs)

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

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

Этот пример исключает все записи, чьё поле pub_date позже 2005-01-03 И чьё поле 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-01-03 ИЛИ чьё поле заголовок равно “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)
Новая функция в 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 будет использовать стандартную сортировку связанной модели или сортировать по первичному ключу связанной модели, если не указана Meta.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), которые контролируют порядок сортировки значений NULL.

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

Примечание

Разрешается указывать многозначное поле для сортировки результатов (например, поле 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; каждый 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, который имеет определённый порядок сортировки (например, при запросе к модели, которая определяет порядок по умолчанию, или при использовании 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'}, ...]>

Предупреждение

Поскольку атрибуты 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, чтобы получить результаты в виде 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 имеет разные представления в зависимости от используемого часового пояса. Этот параметр должен быть объектом datetime.tzinfo. Если это None, Django использует текущий часовой пояс. Он не влияет, когда USE_TZ равно False.

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

Устарело начиная с версии 4.0: Параметр is_dst устарел и будет удалён в Django 5.0.

Примечание

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

  • SQLite: нет требований. Преобразования выполняются в Python.
  • PostgreSQL: нет требований (см. Часовые пояса).
  • 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')
END_OF_DOCUMENT_MARKER

Кроме того, допускаются только 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 обратно к объекту, в котором определено поле. Вместо указания имени поля используйте related_name для поля связанного объекта.

Возможны ситуации, когда вы хотите вызвать select_related() с большим количеством связанных объектов или когда вы не знаете все отношения. В этих случаях можно вызвать select_related() без аргументов. Это позволит следовать всем не-NULL внешним ключам, которые могут быть найдены - 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 работает путем создания SQL-соединения и включения полей связанного объекта в SELECT. По этой причине select_related получает связанные объекты в одном запросе к базе данных. Однако, чтобы избежать значительно большего набора результатов, возникающего при соединении через отношение «многие», 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 для каждого элемента в пицце 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 и был выполнен основной запрос.

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

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

Примечание

Помните, что, как всегда при использовании QuerySets, любые последующие методы цепочки, которые предполагают другой запрос к базе данных, игнорируют предварительно кэшированные результаты и извлекают данные с помощью нового запроса к базе данных. Таким образом, если вы пишете следующее:

>>> pizzas = Pizza.objects.prefetch_related('toppings')
>>> [list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]

…тот факт, что pizza.toppings.all() был предварительно извлечен, вам не поможет. prefetch_related('toppings') подразумевает pizza.toppings.all(), но pizza.toppings.filter() — это новый и другой запрос. Кэш предварительного извлечения здесь не поможет; на самом деле это снизит производительность, так как вы выполнили запрос к базе данных, который не использовали. Поэтому используйте эту функцию с осторожностью!

Кроме того, если вы вызываете изменяющие базу данных методы add(), remove(), clear() или set() для related managers, любой предварительно кэшированный результат для отношения будет очищен.

Вы также можете использовать обычный синтаксис объединения для выполнения связанных полей связанных полей. Предположим, что у нас есть дополнительная модель к приведенному выше примеру:

class Restaurant(models.Model):
    pizzas = models.ManyToManyField(Pizza, related_name='restaurants')
    best_pizza = models.ForeignKey(Pizza, related_name='championed_by', on_delete=models.CASCADE)

Следующие операции допустимы:

>>> Restaurant.objects.prefetch_related('pizzas__toppings')

Это предварительно извлечёт все пиццы, принадлежащие ресторанам, и все начинки, принадлежащие этим пиццам. В результате будет выполнено всего 3 запроса к базе данных — один для ресторанов, один для пицц и один для начинок.

>>> Restaurant.objects.prefetch_related('best_pizza__toppings')

Это позволит получить лучшую пиццу и все начинки для лучшей пиццы для каждого ресторана. Это будет сделано в 3 запросах к базе данных — один для ресторанов, один для «лучших пицц» и один для начинок.

Связь best_pizza также можно получить, используя select_related, чтобы уменьшить количество запросов до 2:

>>> Restaurant.objects.select_related('best_pizza').prefetch_related('best_pizza__toppings')

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

Цепочки вызовов prefetch_related будут накапливать запросы, которые предварительно выбираются. Чтобы очистить любое поведение prefetch_related, передайте None в качестве параметра:

>>> non_prefetched = qs.prefetch_related(None)

Одно отличие при использовании prefetch_related заключается в том, что объекты, созданные запросом, могут быть разделены между различными объектами, к которым они относятся, т. е. один экземпляр модели Python может появиться более чем в одной точке дерева объектов, которые возвращаются. Это обычно происходит со связями с внешним ключом. Обычно это поведение не является проблемой и, на самом деле, экономит память и время процессора.

Хотя prefetch_related поддерживает предварительную выборку GenericForeignKey отношений, количество запросов будет зависеть от данных. Поскольку GenericForeignKey может ссылаться на данные в нескольких таблицах, требуется по одному запросу на каждую таблицу, на которую ссылаются, а не один запрос для всех элементов. Могут быть дополнительные запросы к таблице ContentType, если соответствующие строки еще не были получены.

prefetch_related в большинстве случаев будет реализовано с помощью запроса SQL, который использует оператор «IN». Это означает, что для большого QuerySet может быть сгенерировано большое выражение «IN», что в зависимости от базы данных может иметь собственные проблемы с производительностью при анализе или выполнении запроса SQL. Всегда проводите профилирование для вашего случая использования!

Обратите внимание, что если вы используете iterator() для выполнения запроса, вызовы prefetch_related() будут проигнорированы, поскольку эти две оптимизации не имеют смысла вместе.

Вы можете использовать объект Prefetch для дальнейшего управления операцией предварительной выборки.

В своей самой простой форме Prefetch эквивалентен традиционным строковым запросам:

>>> from django.db.models import Prefetch
>>> Restaurant.objects.prefetch_related(Prefetch('pizzas__toppings'))

Вы можете предоставить пользовательский набор результатов с необязательным аргументом queryset. Это можно использовать для изменения стандартного порядка сортировки набора результатов:

>>> Restaurant.objects.prefetch_related(
...     Prefetch('pizzas__toppings', queryset=Toppings.objects.order_by('name')))

Или вызвать select_related(), где это уместно, чтобы еще больше сократить количество запросов:

>>> Pizza.objects.prefetch_related(
...     Prefetch('restaurants', queryset=Restaurant.objects.select_related('best_pizza')))

Вы также можете назначить результат предварительной выборки пользовательскому атрибуту с необязательным аргументом to_attr. Результат будет сохранен непосредственно в списке.

Это позволяет многократно выполнять предварительную выборку одного и того же отношения с разными QuerySet; например:

>>> vegetarian_pizzas = Pizza.objects.filter(vegetarian=True)
>>> Restaurant.objects.prefetch_related(
...     Prefetch('pizzas', to_attr='menu'),
...     Prefetch('pizzas', queryset=vegetarian_pizzas, to_attr='vegetarian_menu'))

Запросы, созданные с помощью пользовательских to_attr, по-прежнему могут быть пройдены другими запросами как обычно:

>>> vegetarian_pizzas = Pizza.objects.filter(vegetarian=True)
>>> Restaurant.objects.prefetch_related(
...     Prefetch('pizzas', queryset=vegetarian_pizzas, to_attr='vegetarian_menu'),
...     'vegetarian_menu__toppings')

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

>>> queryset = Pizza.objects.filter(vegetarian=True)
>>>
>>> # Recommended:
>>> restaurants = Restaurant.objects.prefetch_related(
...     Prefetch('pizzas', queryset=queryset, to_attr='vegetarian_pizzas'))
>>> vegetarian_pizzas = restaurants[0].vegetarian_pizzas
>>>
>>> # Not recommended:
>>> restaurants = Restaurant.objects.prefetch_related(
...     Prefetch('pizzas', queryset=queryset))
>>> vegetarian_pizzas = restaurants[0].pizzas.all()

Пользовательская предварительная выборка также работает с одиночными связанными отношениями, такими как прямые ForeignKey или OneToOneField. Обычно для этих отношений следует использовать select_related(), но есть ряд случаев, когда предварительная выборка с пользовательской QuerySet полезна:

  • Вы хотите использовать QuerySet, который выполняет дальнейшую предварительную выборку связанных моделей.
  • Вы хотите предварительно выбрать только подмножество связанных объектов.
  • Вы хотите использовать методы оптимизации производительности, такие как deferred fields:

    >>> queryset = Pizza.objects.only('name')
    >>>
    >>> restaurants = Restaurant.objects.prefetch_related(
    ...     Prefetch('best_pizza', queryset=queryset))
    

При использовании нескольких баз данных Prefetch будет учитывать ваш выбор базы данных. Если внутренний запрос не указывает базу данных, он будет использовать базу данных, выбранную внешним запросом. Все следующие варианты допустимы:

>>> # Both inner and outer queries will use the 'replica' database
>>> Restaurant.objects.prefetch_related('pizzas__toppings').using('replica')
>>> Restaurant.objects.prefetch_related(
...     Prefetch('pizzas__toppings'),
... ).using('replica')
>>>
>>> # Inner will use the 'replica' database; outer will use 'default' database
>>> Restaurant.objects.prefetch_related(
...     Prefetch('pizzas__toppings', queryset=Toppings.objects.using('replica')),
... )
>>>
>>> # Inner will use 'replica' database; outer will use 'cold-storage' database
>>> Restaurant.objects.prefetch_related(
...     Prefetch('pizzas__toppings', queryset=Toppings.objects.using('replica')),
... ).using('cold-storage')

Примечание

Порядок запросов имеет значение.

Рассмотрим следующие примеры:

>>> prefetch_related('pizzas__toppings', 'pizzas')

Это работает, даже если оно не упорядочено, потому что 'pizzas__toppings' уже содержит всю необходимую информацию, поэтому второй аргумент 'pizzas' фактически избыточен.

>>> prefetch_related('pizzas__toppings', Prefetch('pizzas', queryset=Pizza.objects.all()))

Это приведет к ошибке ValueError из-за попытки переопределить набор результатов ранее увиденного запроса. Обратите внимание, что неявный набор результатов был создан для обработки 'pizzas' как части запроса 'pizzas__toppings'.

>>> prefetch_related('pizza_list__toppings', Prefetch('pizzas', to_attr='pizza_list'))

Это вызовет ошибку AttributeError, потому что 'pizza_list' еще не существует, когда обрабатывается 'pizza_list__toppings'.

Это соображение не ограничивается использованием объектов Prefetch. Некоторые продвинутые методы могут потребовать выполнения запросов в определенном порядке, чтобы избежать создания дополнительных запросов; поэтому рекомендуется всегда тщательно упорядочивать аргументы prefetch_related.

extra()

extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

Иногда синтаксис запросов Django сам по себе не может легко выразить сложные WHERE. Для этих крайних случаев Django предоставляет модификатор extra() QuerySet — крючок для вставки конкретных предложений в SQL, генерируемый QuerySet.

Используйте этот метод в крайних случаях

Это старый API, который мы планируем в какой-то момент в будущем сделать устаревшим. Используйте его только если вы не можете выразить свой запрос с помощью других методов набора результатов. Если вам все же нужно его использовать, пожалуйста, создайте запрос с помощью ключевого слова QuerySet.extra со своим случаем использования (сначала проверьте список существующих запросов), чтобы мы могли улучшить API QuerySet, чтобы позволить удалить extra(). Мы больше не будем улучшать или исправлять ошибки для этого метода.

Например, это использование extra():

>>> qs.extra(
...     select={'val': "select col from sometable where othercol = %s"},
...     select_params=(someparam,),
... )

эквивалентно:

>>> qs.annotate(val=RawSQL("select col from sometable where othercol = %s", (someparam,)))

Основное преимущество использования RawSQL заключается в том, что вы можете задать output_field при необходимости. Основной недостаток заключается в том, что если вы ссылаетесь на какой-либо псевдоним таблицы набора результатов в сыром SQL, то Django может изменить этот псевдоним (например, когда набор результатов используется в качестве подзапроса в другом запросе).

Предупреждение

Вы должны быть очень осторожны при использовании extra(). Каждый раз при его использовании вы должны экранировать любые управляемые пользователем параметры, используя params, чтобы защититься от атак SQL-инъекции.

Вы также не должны заключать в кавычки заменители в строке SQL. Этот пример уязвим для SQL-инъекции из-за кавычек вокруг %s:

SELECT col FROM sometable WHERE othercol = '%s'  # unsafe!

Вы можете узнать больше о том, как работает защита от SQL-инъекций Django, в защите от SQL-инъекций.

По определению, эти дополнительные запросы могут быть не переносимыми на разные движки баз данных (поскольку вы явно пишете код SQL) и нарушают принцип DRY, поэтому следует избегать их, если это возможно.

Укажите один или несколько из params, select, where или tables. Ни один из аргументов не является обязательным, но вы должны использовать по крайней мере один из них.

END_OF_DOCUMENT_MARKER
  • select

    Аргумент select позволяет добавлять дополнительные поля в клаузу SELECT. Он должен быть словарем, сопоставляющим имена атрибутов с SQL-клаузами, используемыми для вычисления этого атрибута.

    Пример:

    Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
    

    В результате каждый объект Entry будет иметь дополнительный атрибут is_recent, булево значение, указывающее, является ли дата записи pub_date позже 1 января 2006 года.

    Django вставляет предоставленный SQL-фрагмент непосредственно в утверждение SELECT, поэтому результирующий SQL-запрос в приведенном выше примере будет чем-то вроде:

    SELECT blog_entry.*, (pub_date > '2006-01-01') AS is_recent
    FROM blog_entry;
    

    Следующий пример более сложный; он использует подзапрос, чтобы добавить каждому результирующему объекту Blog атрибут entry_count, целое число, представляющее количество связанных объектов Entry:

    Blog.objects.extra(
        select={
            'entry_count': 'SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id'
        },
    )
    

    В этом конкретном случае мы используем тот факт, что запрос уже содержит таблицу blog_blog в своей клаузе FROM.

    Результирующий SQL-запрос в приведенном выше примере будет следующим:

    SELECT blog_blog.*, (SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id) AS entry_count
    FROM blog_blog;
    

    Обратите внимание, что скобки, необходимые большинству СУБД вокруг подзапросов, не требуются в клаузах select Django. Также обратите внимание, что некоторые базы данных, такие как некоторые версии MySQL, не поддерживают подзапросы.

    В некоторых редких случаях вам может потребоваться передать параметры в SQL-фрагменты в extra(select=...). Для этой цели используйте параметр select_params.

    Это будет работать, например:

    Blog.objects.extra(
        select={'a': '%s', 'b': '%s'},
        select_params=('one', 'two'),
    )
    

    Если вам нужно использовать литеральный %s внутри строки запроса, используйте последовательность %%s.

  • where / tables

    Вы можете определить явные SQL-WHERE клаузы — возможно, для выполнения неявных соединений — с помощью where. Вы можете вручную добавлять таблицы в клаузу SQL FROM, используя tables.

    where и tables оба принимают список строк. Все where параметры «И» связаны с другими критериями поиска.

    Пример:

    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
    

    …переводится (приблизительно) в следующий SQL-запрос:

    SELECT * FROM blog_entry WHERE (foo='a' OR bar='a') AND (baz='a')
    

    Будьте осторожны при использовании параметра tables, если вы указываете таблицы, которые уже используются в запросе. Когда вы добавляете дополнительные таблицы через параметр tables, Django предполагает, что вы хотите включить эту таблицу ещё раз, если она уже включена. Это создаёт проблему, поскольку имя таблицы будет затем получено псевдоним. Если таблица появляется несколько раз в SQL-запросе, второй и последующие случаи должны использовать псевдонимы, чтобы база данных могла их различать. Если вы ссылаетесь на дополнительную таблицу, добавленную в параметр where, это приведёт к ошибкам.

    Как правило, вы будете добавлять только дополнительные таблицы, которые ещё не появлялись в запросе. Однако, если возникнет описанная выше ситуация, есть несколько решений. Сначала посмотрите, можно ли обойтись без включения дополнительной таблицы и использовать уже существующую в запросе. Если это невозможно, поместите вызов extra() в начало построения набора запросов, чтобы ваша таблица была первым использованием этой таблицы. Наконец, если всё остальное не сработает, посмотрите на сгенерированный запрос и перепишите добавление where, чтобы использовать псевдоним, присвоенный вашей дополнительной таблице. Псевдоним будет одинаковым каждый раз, когда вы создаёте набор запросов одинаковым способом, поэтому вы можете полагаться на имя псевдонима, которое не изменится.

  • order_by

    Если вам нужно отсортировать результирующий набор запросов, используя некоторые из новых полей или таблиц, добавленных с помощью extra(), используйте параметр order_by к extra() и передайте последовательность строк. Эти строки должны быть либо полями модели (как в обычном методе order_by() наборах запросов), либо в формате table_name.column_name, либо псевдонимом столбца, указанного в параметре select к extra().

    Например:

    q = Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
    q = q.extra(order_by = ['-is_recent'])
    

    Это отсортирует все элементы, для которых is_recent равно true, в начале результата (True сортируется перед False в порядке убывания).

    Кстати, это демонстрирует, что вы можете сделать несколько вызовов к extra(), и он будет вести себя ожидаемым образом (каждый раз добавляя новые ограничения).

  • params

    В параметре where, описанном выше, могут использоваться стандартные символы подстановки строк Python — '%s', чтобы указать параметры, которые система управления базами данных должна автоматически обработать. Аргумент params — это список дополнительных параметров для подстановки.

    Пример:

    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
    

    Всегда используйте params вместо встраивания значений непосредственно в where, так как params гарантирует правильное цитирование значений в соответствии с вашей конкретной базой данных. Например, кавычки будут правильно экранированы.

    Плохо:

    Entry.objects.extra(where=["headline='Lennon'"])
    

    Хорошо:

    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
    

Предупреждение

Если вы выполняете запросы в MySQL, обратите внимание, что неявное приведение типов в MySQL может привести к неожиданным результатам при смешивании типов. Если вы запрашиваете столбец строкового типа, но со значением целого числа, MySQL приведёт типы всех значений в таблице к целочисленному типу перед выполнением сравнения. Например, если ваша таблица содержит значения 'abc', 'def', и вы запрашиваете WHERE mycolumn=0, обе строки будут соответствовать. Чтобы предотвратить это, выполните корректное приведение типов перед использованием значения в запросе.

defer()

defer(*fields)

В некоторых сложных ситуациях моделирования данных ваши модели могут содержать множество полей, некоторые из которых могут содержать много данных (например, текстовые поля) или требуют дорогостоящей обработки для преобразования в объекты Python. Если вы используете результаты набора запросов в ситуации, где вам неизвестно, нужны ли вам эти поля при первоначальной загрузке данных, вы можете указать Django не извлекать их из базы данных.

Для этого передайте имена полей, которые не нужно загружать, в defer():

Entry.objects.defer("headline", "body")

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

Вы можете сделать несколько вызовов к defer(). Каждый вызов добавляет новые поля в отложенный набор:

# Defers both the body and headline fields.
Entry.objects.defer("body").filter(rating=5).defer("headline")

Порядок добавления полей в отложенный набор не имеет значения. Вызов defer() с именем поля, которое уже отложено, не повлияет на результат (поле всё ещё будет отложено).

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

Blog.objects.select_related().defer("entry__headline", "entry__body")

Если вы хотите очистить набор отложенных полей, передайте None в качестве параметра к defer():

# Load all fields immediately.
my_queryset.defer(None)

Некоторые поля в модели не будут отложены, даже если вы их запросите. Вы никогда не можете отложить загрузку первичного ключа. Если вы используете select_related() для извлечения связанных моделей, вы не должны откладывать загрузку поля, которое соединяет первичную модель с связанной, так как это приведёт к ошибке.

Примечание

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

Даже если вы считаете, что вы в продвинутой ситуации, используйте defer() только тогда, когда на этапе создания набора запросов вы не можете определить, нужны ли вам дополнительные поля или нет. Если вы часто загружаете и используете определенный подмножество ваших данных, лучший выбор — нормализовать ваши модели и поместить не загруженные данные в отдельную модель (и таблицу базы данных). Если столбцы обязательно должны оставаться в одной таблице по какой-либо причине, создайте модель с Meta.managed = False (см. документацию managed attribute) содержащей только поля, которые вам обычно нужно загружать, и используйте её там, где вы могли бы вызвать defer(). Это делает ваш код более ясным для читателя, немного быстрее и потребляет немного меньше памяти в процессе Python.

Например, обе эти модели используют одну и ту же базу данных:

class CommonlyUsedModel(models.Model):
    f1 = models.CharField(max_length=10)

    class Meta:
        managed = False
        db_table = 'app_largetable'

class ManagedModel(models.Model):
    f1 = models.CharField(max_length=10)
    f2 = models.CharField(max_length=10)

    class Meta:
        db_table = 'app_largetable'

# Two equivalent QuerySets:
CommonlyUsedModel.objects.all()
ManagedModel.objects.all().defer('f2')

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

Примечание

При вызове save() для экземпляров с отложенными полями, будут сохранены только загруженные поля. Подробности см. в save().

only()

only(*fields)

Метод only() в большей степени является противоположностью defer(). Вызывается с полями, которые не должны быть отложены при извлечении модели. Если у вас есть модель, где почти все поля должны быть отложены, использование only() для указания дополнительного набора полей может привести к более простому коду.

Предположим, у вас есть модель с полями name, age и biography. Два следующих набора запросов эквивалентны по отложенным полям:

Person.objects.defer("age", "biography")
Person.objects.only("name")

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

# This will defer all fields except the headline.
Entry.objects.only("body", "rating").only("headline")

Поскольку defer() действует поэтапно (добавляя поля в отложенный список), вы можете комбинировать вызовы only() и defer(), и всё будет работать логично:

# Final result is that everything except "headline" is deferred.
Entry.objects.only("headline", "body").defer("body")

# Final result loads headline and body immediately (only() replaces any
# existing set of fields).
Entry.objects.defer("body").only("headline", "body")

Все предосторожности, указанные в примечании к документации для defer(), также применяются к only(). Используйте его с осторожностью и только после исчерпания других вариантов.

Использование only() и опущение поля, запрошенного с помощью select_related(), также является ошибкой.

Примечание

При вызове save() для экземпляров с отложенными полями, будут сохранены только загруженные поля. Дополнительные сведения см. в save().

using()

using(alias)

Этот метод предназначен для управления базой данных, к которой будет применяться QuerySet, если вы используете более одной базы данных. Единственным аргументом этого метода является псевдоним базы данных, как определено в DATABASES.

Например:

# queries the database with the 'default' alias.
>>> Entry.objects.all()

# queries the database with the 'backup' alias
>>> Entry.objects.using('backup')

select_for_update()

select_for_update(nowait=False, skip_locked=False, of=(), no_key=False)

Возвращает запрос, который будет блокировать строки до конца транзакции, генерируя оператор SELECT ... FOR UPDATE SQL на поддерживаемых базах данных.

Например:

from django.db import transaction

entries = Entry.objects.select_for_update().filter(author=request.user)
with transaction.atomic():
    for entry in entries:
        ...

Когда запрос оценивается (for entry in entries в данном случае), все соответствующие записи будут заблокированы до конца блока транзакции, что означает, что другие транзакции будут предотвращены от изменения или получения блокировок на них.

Обычно, если другая транзакция уже получила блокировку на одной из выбранных строк, запрос будет ожидать, пока блокировка не будет освобождена. Если это не то поведение, которое вам нужно, вызовите select_for_update(nowait=True). Это сделает вызов неблокирующим. Если конфликтная блокировка уже получена другой транзакцией, при оценке запроса будет поднято исключение DatabaseError. Вы также можете игнорировать заблокированные строки, используя select_for_update(skip_locked=True) вместо. nowait и skip_locked взаимоисключающие, и попытка вызвать select_for_update() с включенными обоими вариантами приведёт к возникновению ValueError.

По умолчанию select_for_update() блокирует все строки, выбранные запросом. Например, строки связанных объектов, указанные в select_related(), блокируются в дополнение к строкам модели запроса. Если это нежелательно, укажите связанные объекты, которые вы хотите заблокировать в select_for_update(of=(...)), используя тот же синтаксис полей, что и в select_related(). Используйте значение 'self' для ссылки на модель запроса.

Блокировка родительских моделей в select_for_update(of=(...))

Если вам нужно заблокировать родительские модели при использовании наследования от нескольких таблиц, вы должны указать поля родительской ссылки (по умолчанию <parent_model_name>_ptr) в аргументе of. Например:

Restaurant.objects.select_for_update(of=('self', 'place_ptr'))

Использование select_for_update(of=(...)) со специфицированными полями

Если вы хотите заблокировать модели и указать выбранные поля, например, используя values(), вы должны выбрать по меньшей мере одно поле из каждой модели в аргументе of. Модели без выбранных полей не будут заблокированы.

Только в PostgreSQL, вы можете передать no_key=True, чтобы получить более слабую блокировку, которая всё ещё позволяет создавать строки, которые просто ссылаются на заблокированные строки (например, через внешний ключ), в то время как блокировка активна. Дополнительные сведения о режимах блокировки строк см. в документации PostgreSQL по адресу row-level lock modes.

Вы не можете использовать select_for_update() с допускающими значения NULL отношениями:

>>> Person.objects.select_related('hometown').select_for_update()
Traceback (most recent call last):
...
django.db.utils.NotSupportedError: FOR UPDATE cannot be applied to the nullable side of an outer join

Чтобы избежать этого ограничения, вы можете исключить нулевые объекты, если они вам не нужны:

>>> Person.objects.select_related('hometown').select_for_update().exclude(hometown=None)
<QuerySet [<Person: ...)>, ...]>

postgresql, oracle и mysql бэкэнды баз данных поддерживают select_for_update(). Однако MariaDB 10.3+ поддерживает только аргумент nowait, MariaDB 10.6+ также поддерживает аргумент skip_locked, а MySQL 8.0.1+ поддерживает аргументы nowait, skip_locked и of. Аргумент no_key поддерживается только в PostgreSQL.

Передача nowait=True, skip_locked=True, no_key=True или of в select_for_update() с использованием бэкэндов баз данных, которые не поддерживают эти параметры, таких как MySQL, вызывает NotSupportedError. Это предотвращает непредвиденное блокирование кода.

Оценка запроса с select_for_update() в режиме автокоммита в бэкэндах, которые поддерживают SELECT ... FOR UPDATE, является ошибкой TransactionManagementError, так как в этом случае строки не блокируются. Если это разрешено, это могло бы привести к повреждению данных и легко могло быть вызвано кодом, который ожидает выполнения вне транзакции.

Использование select_for_update() в бэкэндах, которые не поддерживают SELECT ... FOR UPDATE (например, SQLite), не повлияет. SELECT ... FOR UPDATE не будет добавлен в запрос, и ошибка не возникает, если select_for_update() используется в режиме автокоммита.

Предупреждение

Хотя select_for_update() обычно терпит неудачу в режиме автокоммита, так как TestCase автоматически оборачивает каждый тест в транзакцию, вызов select_for_update() в TestCase даже вне блока atomic() пройдёт (возможно, неожиданно), не вызывая TransactionManagementError. Чтобы правильно протестировать select_for_update(), следует использовать TransactionTestCase.

Некоторые выражения могут не поддерживаться

PostgreSQL не поддерживает select_for_update() с выражениями Window.

Изменено в Django 3.2:

Был добавлен аргумент no_key.

Аргумент of был разрешен для MySQL 8.0.1+.

Изменено в Django 4.0:

Аргумент skip_locked был разрешен для MariaDB 10.6+.

raw()

raw(raw_query, params=(), translations=None, using=None)

Принимает SQL-запрос в сыром виде, выполняет его и возвращает экземпляр django.db.models.query.RawQuerySet. Этот экземпляр RawQuerySet можно перебирать, как и обычный QuerySet, чтобы получить экземпляры объектов.

Для получения дополнительной информации см. Выполнение SQL-запросов в сыром виде.

Предупреждение

raw() всегда запускает новый запрос и не учитывает предыдущую фильтрацию. Поэтому его обычно следует вызывать из Manager или из нового экземпляра QuerySet.

Изменено в Django 3.2:

Значение по умолчанию аргумента params было изменено с None на пустую кортеж.

Операторы, возвращающие новые QuerySet

Объединённые запросы должны использовать одну и ту же модель.

И (&)

Объединяет два QuerySet с помощью оператора SQL AND.

Следующее эквивалентно:

Model.objects.filter(x=1) & Model.objects.filter(y=2)
Model.objects.filter(x=1, y=2)
from django.db.models import Q
Model.objects.filter(Q(x=1) & Q(y=2))

Эквивалент SQL:

SELECT ... WHERE x=1 AND y=2

ИЛИ (|)

Объединяет два QuerySet с помощью оператора SQL OR.

Следующее эквивалентно:

Model.objects.filter(x=1) | Model.objects.filter(y=2)
from django.db.models import Q
Model.objects.filter(Q(x=1) | Q(y=2))

Эквивалент SQL:

SELECT ... WHERE x=1 OR y=2

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

Следующие QuerySet методы оценивают QuerySet и возвращают что-то кроме QuerySet.

Эти методы не используют кэш (см. Кэширование и наборы запросов). Вместо этого они каждый раз обращаются к базе данных.

get()

get(*args, **kwargs)

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

Entry.objects.get(id=1)
Entry.objects.get(Q(blog=blog) & Q(entry_number=1))

Если ожидается, что набор запросов уже вернёт одну строку, можно использовать get() без аргументов, чтобы получить объект для этой строки:

Entry.objects.filter(pk=1).get()

Если get() не находит ни одного объекта, он поднимает исключение Model.DoesNotExist:

Entry.objects.get(id=-999) # raises Entry.DoesNotExist

Если get() находит более одного объекта, он поднимает исключение Model.MultipleObjectsReturned:

Entry.objects.get(name='A Duplicated Name') # raises Entry.MultipleObjectsReturned

Оба этих класса исключений являются атрибутами класса модели и специфичны для этой модели. Если вы хотите обрабатывать такие исключения из нескольких get() вызовов для разных моделей, можно использовать их общие базовые классы. Например, можно использовать django.core.exceptions.ObjectDoesNotExist для обработки исключений DoesNotExist из нескольких моделей:

from django.core.exceptions import ObjectDoesNotExist

try:
    blog = Blog.objects.get(id=1)
    entry = Entry.objects.get(blog=blog, entry_number=1)
except ObjectDoesNotExist:
    print("Either the blog or entry doesn't exist.")

create()

create(**kwargs)

Удобный метод для создания объекта и сохранения его в одном шаге. Таким образом:

p = Person.objects.create(first_name="Bruce", last_name="Springsteen")

и:

p = Person(first_name="Bruce", last_name="Springsteen")
p.save(force_insert=True)

эквивалентны.

Параметр force_insert документирован в другом месте, но всё, что он означает, это то, что новый объект всегда будет создан. Обычно об этом можно не беспокоиться. Однако, если ваша модель содержит значение первичного ключа, заданное вручную, и это значение уже существует в базе данных, вызов create() завершится с ошибкой IntegrityError, так как первичные ключи должны быть уникальными. Будьте готовы обработать исключение, если вы используете ручные первичные ключи.

get_or_create()

get_or_create(defaults=None, **kwargs)

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

Возвращает кортеж из (object, created), где object — полученный или созданный объект, а created — булево значение, указывающее, был ли создан новый объект.

Этот метод предназначен для предотвращения дублирования объектов при одновременных запросах и как сокращение для шаблонного кода. Например:

try:
    obj = Person.objects.get(first_name='John', last_name='Lennon')
except Person.DoesNotExist:
    obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
    obj.save()

Здесь, при одновременных запросах, могут быть предприняты несколько попыток сохранить Person с одинаковыми параметрами. Чтобы избежать этой гонки, вышеприведённый пример можно переписать, используя get_or_create() следующим образом:

obj, created = Person.objects.get_or_create(
    first_name='John',
    last_name='Lennon',
    defaults={'birthday': date(1940, 10, 9)},
)

Любые ключевые аргументы, переданные get_or_create() — за исключением необязательного, называемого defaults — будут использованы в вызове get(). Если объект найден, get_or_create() возвращает кортеж этого объекта и False.

Предупреждение

Этот метод предполагает атомарность, исходя из предположения, что база данных обеспечивает уникальность ключевых аргументов (см. unique или unique_together). Если поля, используемые в ключевых аргументах, не имеют ограничения уникальности, одновременные вызовы этого метода могут привести к вставке нескольких строк с одинаковыми параметрами.

Можно указать более сложные условия для полученного объекта, объединив get_or_create() с filter() и используя Q objects. Например, чтобы получить Роберта или Боба Марли, если любой из них существует, и создать последнего в противном случае:

from django.db.models import Q

obj, created = Person.objects.filter(
    Q(first_name='Bob') | Q(first_name='Robert'),
).get_or_create(last_name='Marley', defaults={'first_name': 'Bob'})

Если найдено несколько объектов, get_or_create() поднимает MultipleObjectsReturned. Если объект не найден, get_or_create() создаст и сохранит новый объект, вернув кортеж нового объекта и True. Новый объект будет создан примерно по этому алгоритму:

params = {k: v for k, v in kwargs.items() if '__' not in k}
params.update({k: v() if callable(v) else v for k, v in defaults.items()})
obj = self.model(**params)
obj.save()

Другими словами, начните с любого не-'defaults' ключевого аргумента, не содержащего двойного подчёркивания (что указывает на неточный поиск). Затем добавьте содержимое defaults, переопределяя ключи при необходимости, и используйте результат в качестве ключевых аргументов для класса модели. Если в defaults есть вызываемые объекты, вычислите их. Как упоминалось выше, это упрощение алгоритма, используемого, но оно содержит все существенные детали. Внутренняя реализация имеет больше проверок ошибок, чем это, и обрабатывает некоторые дополнительные граничные случаи; если вас это интересует, прочитайте код.

Если у вас есть поле, названное defaults, и вы хотите использовать его в точном поиске в get_or_create(), используйте 'defaults__exact', как показано ниже:

Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})

Метод get_or_create() имеет поведение с ошибками, аналогичное create(), когда вы используете вручную заданные первичные ключи. Если объект нужно создать, а ключ уже существует в базе данных, будет поднято IntegrityError.

Наконец, несколько слов об использовании get_or_create() в представлениях Django. Пожалуйста, используйте его только в POST запросах, если у вас нет веской причины не делать этого. GET запросы не должны оказывать никакого влияния на данные. Вместо этого используйте POST всякий раз, когда запрос на страницу имеет побочный эффект на ваши данные. Подробнее см. Безопасные методы в спецификации HTTP.

Предупреждение

Вы можете использовать get_or_create() через атрибуты ManyToManyField и обратные связи. В этом случае вы ограничите запросы в контексте этого отношения. Это может привести к проблемам целостности, если вы не используете его последовательно.

Рассмотрим следующие модели:

class Chapter(models.Model):
    title = models.CharField(max_length=255, unique=True)

class Book(models.Model):
    title = models.CharField(max_length=256)
    chapters = models.ManyToManyField(Chapter)

Можно использовать get_or_create() через поле chapters книги, но оно будет получать информацию только в контексте этой книги:

>>> book = Book.objects.create(title="Ulysses")
>>> book.chapters.get_or_create(title="Telemachus")
(<Chapter: Telemachus>, True)
>>> book.chapters.get_or_create(title="Telemachus")
(<Chapter: Telemachus>, False)
>>> Chapter.objects.create(title="Chapter 1")
<Chapter: Chapter 1>
>>> book.chapters.get_or_create(title="Chapter 1")
# Raises IntegrityError

Это происходит потому, что он пытается получить или создать «Главу 1» через книгу «Улисс», но не может сделать ни того, ни другого: отношение не может получить эту главу, потому что она не связана с этой книгой, но не может её создать, так как поле title должно быть уникальным.

update_or_create()

update_or_create(defaults=None, **kwargs)

Удобный метод для обновления объекта с заданными kwargs, создавая новый, если необходимо. defaults — это словарь пар (поле, значение), используемых для обновления объекта. Значения в defaults могут быть вызываемыми объектами.

Возвращает кортеж (object, created), где object — созданный или обновлённый объект, а created — булево значение, указывающее, был ли создан новый объект.

Метод update_or_create пытается извлечь объект из базы данных на основе заданных kwargs. Если совпадение найдено, он обновляет поля, переданные в словаре defaults.

Это сокращение для шаблонного кода. Например:

defaults = {'first_name': 'Bob'}
try:
    obj = Person.objects.get(first_name='John', last_name='Lennon')
    for key, value in defaults.items():
        setattr(obj, key, value)
    obj.save()
except Person.DoesNotExist:
    new_values = {'first_name': 'John', 'last_name': 'Lennon'}
    new_values.update(defaults)
    obj = Person(**new_values)
    obj.save()

Эта модель становится довольно громоздкой по мере увеличения числа полей в модели. Вышеприведённый пример можно переписать, используя update_or_create() так:

obj, created = Person.objects.update_or_create(
    first_name='John', last_name='Lennon',
    defaults={'first_name': 'Bob'},
)

Подробное описание того, как имена, переданные в kwargs, разрешаются, см. в get_or_create().

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

Как и get_or_create() и create(), если вы используете вручную заданные первичные ключи и объект нужно создать, но ключ уже существует в базе данных, будет поднято IntegrityError.

bulk_create()

bulk_create(objs, batch_size=None, ignore_conflicts=False)

Этот метод вставляет предоставленный список объектов в базу данных эффективным способом (как правило, только 1 запрос, независимо от количества объектов) и возвращает созданные объекты в виде списка в том же порядке, что и предоставленный:

>>> objs = Entry.objects.bulk_create([
...     Entry(headline='This is a test'),
...     Entry(headline='This is only a test'),
... ])

Однако это имеет ряд ограничений:

  • Метод модели save() не будет вызван, и сигналы pre_save и post_save не будут отправлены.
  • Он не работает с дочерними моделями в сценарии наследования с несколькими таблицами.
  • Если первичный ключ модели является AutoField, атрибут первичного ключа может быть получен только на определенных базах данных (в настоящее время PostgreSQL, MariaDB 10.5+ и SQLite 3.35+). На других базах данных он не будет установлен.
  • Он не работает с множественными взаимосвязями «многие ко многим».
  • Он преобразует objs в список, который полностью вычисляет objs, если это генератор. Преобразование позволяет проверить все объекты, чтобы любые объекты с вручную заданным первичным ключом могли быть вставлены первыми. Если вы хотите вставлять объекты партиями, не оценивая весь генератор сразу, вы можете использовать эту технику, при условии, что у объектов нет вручную заданных первичных ключей:

    from itertools import islice
    
    batch_size = 100
    objs = (Entry(headline='Test %s' % i) for i in range(1000))
    while True:
        batch = list(islice(objs, batch_size))
        if not batch:
            break
        Entry.objects.bulk_create(batch, batch_size)
    

Параметр batch_size управляет количеством объектов, создаваемых в одном запросе. По умолчанию все объекты создаются в одной группе, за исключением SQLite, где по умолчанию используется не более 999 переменных на запрос.

На базах данных, которые это поддерживают (все, кроме Oracle), установка параметра ignore_conflicts в значение True сообщает базе данных игнорировать ошибки вставки строк, которые не удовлетворяют ограничениям, таким как дублирование уникальных значений. Включение этого параметра отключает установку первичного ключа для каждого экземпляра модели (если база данных это обычно поддерживает).

Предупреждение

В MySQL и MariaDB установка параметра ignore_conflicts в значение True преобразует определенные типы ошибок, отличные от ошибки дублирования ключа, в предупреждения. Даже в режиме строгости. Например: недопустимые значения или нарушения не допускаемых значений. Для получения дополнительной информации см. документацию MySQL и документацию MariaDB.

Изменено в Django 4.0:

Добавлена поддержка извлечения атрибутов первичного ключа в SQLite 3.35+.

bulk_update()

bulk_update(objs, fields, batch_size=None)

Этот метод эффективно обновляет указанные поля в предоставленных экземплярах модели, как правило, одним запросом, и возвращает количество обновленных объектов:

>>> objs = [
...    Entry.objects.create(headline='Entry 1'),
...    Entry.objects.create(headline='Entry 2'),
... ]
>>> objs[0].headline = 'This is entry 1'
>>> objs[1].headline = 'This is entry 2'
>>> Entry.objects.bulk_update(objs, ['headline'])
2
Изменено в Django 4.0:

Добавлено возвращаемое значение — количество обновленных объектов.

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

  • Вы не можете обновить первичный ключ модели.
  • Метод save() каждой модели не вызывается, и сигналы pre_save и post_save не отправляются.
  • Если обновляется большое количество столбцов в большом количестве строк, сгенерированный SQL может быть очень большим. Избегайте этого, указав подходящий batch_size.
  • Обновление полей, определенных в предках наследования с несколькими таблицами, потребует дополнительного запроса на предка.
  • Если в отдельной группе содержатся дубликаты, только первый экземпляр в этой группе приведет к обновлению.
  • Количество обновленных объектов, возвращаемых функцией, может быть меньше, чем количество переданных объектов. Это может быть вызвано передачей дублирующих объектов, которые обновляются в одной группе, или проблемами с конкурентным доступом, в результате которых объекты больше не присутствуют в базе данных.

Параметр batch_size управляет количеством объектов, сохраняемых в одном запросе. По умолчанию все объекты обновляются в одной группе, за исключением SQLite и Oracle, которые имеют ограничения на количество переменных, используемых в запросе.

count()

count()

Возвращает целое число, представляющее количество объектов в базе данных, соответствующих QuerySet.

Пример:

# Returns the total number of entries in the database.
Entry.objects.count()

# Returns the number of entries whose headline contains 'Lennon'
Entry.objects.filter(headline__contains='Lennon').count()

Вызов count() выполняет запрос SELECT COUNT(*) за кулисами, поэтому всегда следует использовать count(), а не загружать все записи в объекты Python и вызывать len() на результате (если вы не хотите загружать объекты в память, в этом случае len() будет быстрее).

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

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

in_bulk()

in_bulk(id_list=None, *, field_name='pk')

Принимает список значений поля (id_list) и field_name для этих значений и возвращает словарь, сопоставляющий каждое значение с экземпляром объекта с заданным значением поля. Исключение django.core.exceptions.ObjectDoesNotExist никогда не будет возбуждено методом in_bulk; то есть любое значение id_list, не соответствующее ни одному экземпляру, просто будет проигнорировано. Если id_list не указано, возвращаются все объекты из набора запросов. field_name должен быть уникальным полем или отдельным полем (если в distinct() указано только одно поле). field_name по умолчанию равен первичному ключу.

Пример:

>>> Blog.objects.in_bulk([1])
{1: <Blog: Beatles Blog>}
>>> Blog.objects.in_bulk([1, 2])
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>}
>>> Blog.objects.in_bulk([])
{}
>>> Blog.objects.in_bulk()
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>, 3: <Blog: Django Weblog>}
>>> Blog.objects.in_bulk(['beatles_blog'], field_name='slug')
{'beatles_blog': <Blog: Beatles Blog>}
>>> Blog.objects.distinct('name').in_bulk(field_name='name')
{'Beatles Blog': <Blog: Beatles Blog>, 'Cheddar Talk': <Blog: Cheddar Talk>, 'Django Weblog': <Blog: Django Weblog>}

Если вы передадите in_bulk() пустой список, вы получите пустой словарь.

Изменено в Django 3.2:

Разрешено использование отдельного поля.

iterator()

iterator(chunk_size=2000)

Вычисляет QuerySet (выполняя запрос) и возвращает итератор (см. PEP 234) по результатам. Набор запросов обычно кэширует свои результаты внутри себя, так что повторные вычисления не приводят к дополнительным запросам. В отличие от этого, iterator() будет читать результаты непосредственно, без кэширования на уровне QuerySet (внутри, итератор по умолчанию вызывает iterator() и кэширует возвращаемое значение). Для наборов запросов, возвращающих большое количество объектов, которые вам нужно получить только один раз, это может привести к лучшей производительности и значительному сокращению потребления памяти.

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

Кроме того, использование iterator() игнорирует предыдущие вызовы prefetch_related(), поскольку эти два оптимизация не имеют смысла вместе.

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

С курсорами на стороне сервера

Oracle и PostgreSQL используют курсоры на стороне сервера для передачи результатов из базы данных без загрузки всего набора результатов в память.

Драйвер базы данных Oracle всегда использует курсоры на стороне сервера.

При использовании курсоров на стороне сервера параметр chunk_size задает количество результатов для кэширования на уровне драйвера базы данных. Получение больших блоков уменьшает количество поездок между драйвером базы данных и базой данных, за счет увеличения потребления памяти.

В PostgreSQL курсоры на стороне сервера будут использоваться только тогда, когда значение настройки DISABLE_SERVER_SIDE_CURSORS равно False. Обратитесь к Пулы транзакций и курсоры на стороне сервера, если вы используете пул соединений, настроенный в режиме пула транзакций. Когда курсоры на стороне сервера отключены, поведение такое же, как у баз данных, которые не поддерживают курсоры на стороне сервера.

Без курсоров на стороне сервера

MySQL не поддерживает передачу результатов, поэтому драйвер базы данных Python загружает весь набор результатов в память. Затем набор результатов преобразуется в объекты строк Python адаптером базы данных с использованием метода fetchmany(), определенного в PEP 249.

SQLite может получать результаты партиями с помощью fetchmany(), но так как SQLite не обеспечивает изоляцию между запросами внутри соединения, будьте осторожны при записи в таблицу, по которой осуществляется итерация. Для получения дополнительной информации см. Изоляция при использовании QuerySet.iterator().

Параметр chunk_size управляет размером партий, которые Django получает от драйвера базы данных. Более крупные партии уменьшают накладные расходы на общение с драйвером базы данных за счет небольшого увеличения потребления памяти.

Значение по умолчанию параметра chunk_size, 2000, взято из вычисления на списке рассылки psycopg:

Предполагая строки из 10-20 столбцов с различными текстовыми и числовыми данными, 2000 запросов извлекут менее 100 КБ данных, что является хорошим компромиссом между количеством переданных строк и потерянными данными, если цикл завершится преждевременно.

latest()

latest(*fields)

Возвращает последнюю запись в таблице на основе заданного поля/полей.

Этот пример возвращает последнюю Entry в таблице по полю pub_date:

Entry.objects.latest('pub_date')

Вы также можете выбрать последнюю запись по нескольким полям. Например, чтобы выбрать Entry с самой ранней expire_date, когда две записи имеют одинаковую pub_date:

Entry.objects.latest('pub_date', '-expire_date')

Отрицательный знак в '-expire_date' означает сортировку expire_date в понижающем порядке. Так как latest() получает последний результат, выбирается Entry с самой ранней expire_date.

Если в Meta вашей модели указано get_latest_by, вы можете опустить любые аргументы к earliest() или latest(). Поля, указанные в get_latest_by, будут использоваться по умолчанию.

Как и get(), earliest() и latest() генерируют исключение DoesNotExist, если нет записи с заданными параметрами.

Обратите внимание, что earliest() и latest() существуют только для удобства и читабельности.

earliest() и latest() могут возвращать записи с нулевыми датами.

Поскольку сортировка делегируется базе данных, результаты по полям, допускающим значения NULL, могут сортироваться по-разному, если вы используете разные базы данных. Например, PostgreSQL и MySQL сортируют значения NULL так, как будто они больше, чем ненулевые значения, в то время как SQLite делает наоборот.

Возможно, вам потребуется отфильтровать нулевые значения:

Entry.objects.filter(pub_date__isnull=False).latest('pub_date')

earliest()

earliest(*fields)

В остальном работает так же, как latest(), за исключением изменения направления сортировки.

first()

first()

Возвращает первую запись, соответствующую запросу, или None, если нет подходящей записи. Если в QuerySet нет заданного порядка, то запрос автоматически сортируется по первичному ключу. Это может повлиять на результаты агрегации, как описано в Взаимодействие с order_by().

Пример:

p = Article.objects.order_by('title', 'pub_date').first()

Обратите внимание, что first() — это удобный метод, а следующий фрагмент кода эквивалентен приведенному выше:

try:
    p = Article.objects.order_by('title', 'pub_date')[0]
except IndexError:
    p = None

last()

last()

Работает как first(), но возвращает последнюю запись в запросе.

aggregate()

aggregate(*args, **kwargs)

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

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

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

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

>>> from django.db.models import Count
>>> q = Blog.objects.aggregate(Count('entry'))
{'entry__count': 16}

Используя ключевой аргумент для указания функции агрегации, вы можете управлять именем возвращаемого агрегированного значения:

>>> q = Blog.objects.aggregate(number_of_entries=Count('entry'))
{'number_of_entries': 16}

Для более глубокого обсуждения агрегации см. руководство по теме Агрегация.

exists()

exists()

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

exists() полезно для поиска наличия любых записей в QuerySet, особенно в контексте большого QuerySet.

Чтобы проверить, содержит ли запрос какие-либо элементы:

if some_queryset.exists():
    print("There is at least one object in some_queryset")

Что будет быстрее, чем:

if some_queryset:
    print("There is at least one object in some_queryset")

…но не на большую величину (поэтому для получения преимущества производительности требуется большой запрос).

Кроме того, если запрос some_queryset еще не был оценен, но вы знаете, что будет оценен в какой-то момент, то использование some_queryset.exists() выполнит больше работы (один запрос для проверки существования плюс дополнительный для получения результатов) по сравнению с bool(some_queryset), которое получает результаты и проверяет, были ли возвращены какие-либо результаты.

contains()

contains(obj)
Новое в Django 4.0.

Возвращает True, если QuerySet содержит obj, и False, если нет. Это пытается выполнить запрос максимально простым и быстрым способом.

contains() полезно для проверки принадлежности объекта к QuerySet, особенно в контексте большого QuerySet.

Чтобы проверить, содержит ли запрос определенный элемент:

if some_queryset.contains(obj):
    print('Entry contained in queryset')

Это будет быстрее, чем следующее, которое требует оценки и итерации по всему набору запросов:

if obj in some_queryset:
    print('Entry contained in queryset')

Как и exists(), если some_queryset еще не был оценен, но вы знаете, что будет в какой-то момент, то использование some_queryset.contains(obj) создаст дополнительный запрос к базе данных, что обычно приводит к более медленной общей производительности.

update()

update(**kwargs)

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

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

>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)

(Предполагается, что ваша модель Entry имеет поля pub_date и comments_on.)

Можно обновлять несколько полей — ограничений по количеству нет. Например, здесь мы обновляем поля comments_on и headline:

>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False, headline='This is old')

Метод update() применяется мгновенно, и единственное ограничение на QuerySet, который обновляется, состоит в том, что он может обновлять только столбцы в основной таблице модели, а не в связанных моделях. Например, нельзя сделать так:

>>> Entry.objects.update(blog__name='foo') # Won't work!

Однако фильтрация по связанным полям по-прежнему возможна:

>>> Entry.objects.filter(blog__id=1).update(comments_on=True)

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

Метод update() возвращает количество затронутых строк:

>>> Entry.objects.filter(id=64).update(comments_on=True)
1

>>> Entry.objects.filter(slug='nonexistent-slug').update(comments_on=True)
0

>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
132

Если вам нужно просто обновить запись и не нужно ничего делать с объектом модели, самый эффективный способ — вызвать update() вместо загрузки объекта модели в память. Например, вместо этого:

e = Entry.objects.get(id=10)
e.comments_on = False
e.save()

…сделайте так:

Entry.objects.filter(id=10).update(comments_on=False)

Использование update() также предотвращает состояние гонки, при котором что-то может измениться в вашей базе данных в короткий промежуток времени между загрузкой объекта и вызовом save().

В заключение, обратите внимание, что update() выполняет обновление на уровне SQL и, следовательно, не вызывает никаких save() методов ваших моделей, а также не генерирует сигналы pre_save или post_save (которые являются следствием вызова Model.save()). Если вы хотите обновить набор записей для модели, которая имеет пользовательский метод save(), пройдитесь по ним и вызовите save(), как показано ниже:

for e in Entry.objects.filter(pub_date__year=2010):
    e.comments_on = False
    e.save()
Отсортированный набор результатов запроса
Новое в Django 3.2.

Цепочки order_by() с update() поддерживаются только в MariaDB и MySQL, и игнорируются для других баз данных. Это полезно для обновления уникального поля в указанном порядке без конфликтов. Например:

Entry.objects.order_by('-number').update(number=F('number') + 1)

Примечание

Запрос order_by() будет проигнорирован, если он содержит аннотации, наследуемые поля или запросы, охватывающие связи.

Удаление

delete()

Выполняет запрос SQL на удаление всех строк в наборе QuerySet и возвращает количество удаленных объектов и словарь с количеством удалений по типам объектов.

delete() применяется немедленно. Нельзя вызвать delete() для набора QuerySet, для которого была взята часть среза или который больше не может быть отфильтрован.

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

>>> b = Blog.objects.get(pk=1)

# Delete all the entries belonging to this Blog.
>>> Entry.objects.filter(blog=b).delete()
(4, {'blog.Entry': 2, 'blog.Entry_authors': 2})

По умолчанию Django’s ForeignKey эмулирует SQL ограничение ON DELETE CASCADE — другими словами, все объекты с внешними ключами, указывающими на объекты, которые будут удалены, будут удалены вместе с ними. Например:

>>> blogs = Blog.objects.all()

# This will delete all Blogs and all of their Entry objects.
>>> blogs.delete()
(5, {'blog.Blog': 1, 'blog.Entry': 2, 'blog.Entry_authors': 2})

Это каскадное поведение настраивается с помощью аргумента on_delete к ForeignKey.

Метод delete() выполняет массовое удаление и не вызывает никаких delete() методов ваших моделей. Однако он генерирует сигналы pre_delete и post_delete для всех удаленных объектов (включая каскадные удаления).

Django необходимо загрузить объекты в память, чтобы отправлять сигналы и обрабатывать каскады. Однако, если каскадов и сигналов нет, Django может использовать быстрый путь и удалять объекты без загрузки в память. Для больших удалений это может значительно уменьшить использование памяти. Количество выполненных запросов также может быть уменьшено.

Внешние ключи, установленные на on_delete DO_NOTHING, не препятствуют использованию быстрого пути при удалении.

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

Метод `as_manager`

classmethod as_manager()

Метод класса, который возвращает экземпляр Manager с копией методов QuerySet. Для получения дополнительной информации см. Создание менеджера с методами QuerySet.

Метод `explain`

explain(format=None, **options)

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

Например, при использовании PostgreSQL:

>>> print(Blog.objects.filter(title='My Blog').explain())
Seq Scan on blog  (cost=0.00..35.50 rows=10 width=12)
  Filter: (title = 'My Blog'::bpchar)

Вывод значительно отличается между базами данных.

explain() поддерживается всеми встроенными бэкендами баз данных, кроме Oracle, так как реализация там не является тривиальной.

Параметр format изменяет формат вывода от значения по умолчанию для базы данных, который обычно текстовый. PostgreSQL поддерживает форматы 'TEXT', 'JSON', 'YAML' и 'XML'. MariaDB и MySQL поддерживают форматы 'TEXT' (также называемый 'TRADITIONAL') и 'JSON'. MySQL 8.0.16+ также поддерживает улучшенный формат 'TREE', который похож на вывод 'TEXT' PostgreSQL и используется по умолчанию, если поддерживается.

Некоторые базы данных принимают флаги, которые могут возвращать более подробную информацию о запросе. Передайте эти флаги в качестве ключевых аргументов. Например, при использовании PostgreSQL:

>>> print(Blog.objects.filter(title='My Blog').explain(verbose=True, analyze=True))
Seq Scan on public.blog  (cost=0.00..35.50 rows=10 width=12) (actual time=0.004..0.004 rows=10 loops=1)
  Output: id, title
  Filter: (blog.title = 'My Blog'::bpchar)
Planning time: 0.064 ms
Execution time: 0.058 ms

В некоторых базах данных флаги могут привести к выполнению запроса, что может иметь нежелательные последствия для вашей базы данных. Например, флаг ANALYZE, поддерживаемый MariaDB, MySQL 8.0.18+ и PostgreSQL, может привести к изменениям данных, если есть триггеры или если вызывается функция, даже для запроса SELECT.

Поиск по полям

Поиск по полям — это способ указать основную часть SQL-запроса WHERE. Они указываются в качестве ключевых аргументов для методов QuerySet filter(), exclude() и get().

Для ознакомления, см. Документацию по моделям и запросам к базе данных.

Встроенные средства поиска Django перечислены ниже. Также возможно написать пользовательские средства поиска для полей моделей.

Для удобства, когда тип поиска не указан (как в Entry.objects.get(id=14)), тип поиска по умолчанию считается exact.

Точное совпадение

Точное совпадение. Если предоставленное для сравнения значение равно None, оно будет интерпретировано как SQL NULL (подробнее см. isnull).

Примеры:

Entry.objects.get(id__exact=14)
Entry.objects.get(id__exact=None)

Эквиваленты SQL:

SELECT ... WHERE id = 14;
SELECT ... WHERE id IS NULL;

Сравнения в MySQL

В MySQL настройка «кодировки» таблицы базы данных определяет, являются ли сравнения на основе exact чувствительными к регистру. Это настройка базы данных, а не настройка Django. Можно настроить таблицы MySQL на использование сравнений, чувствительных к регистру, но это связано с некоторыми компромиссами. Подробнее об этом см. в разделе кодировка документации по базам данных.

Нечувствительное к регистру точное совпадение

Нечувствительное к регистру точное совпадение. Если предоставленное для сравнения значение равно None, оно будет интерпретировано как SQL NULL (подробнее см. isnull).

Пример:

Blog.objects.get(name__iexact='beatles blog')
Blog.objects.get(name__iexact=None)

Эквиваленты SQL:

SELECT ... WHERE name ILIKE 'beatles blog';
SELECT ... WHERE name IS NULL;

Обратите внимание, что первый запрос будет соответствовать 'Beatles Blog', 'beatles blog', 'BeAtLes BLoG' и т. д.

Пользователи SQLite

При использовании бэкенда SQLite и строках, не содержащих символов ASCII, имейте в виду примечание к базе данных относительно сравнения строк. SQLite не выполняет нечувствительное к регистру соответствие для строк, не содержащих символов ASCII.

Содержит

Проверка наличия подстроки (чувствительная к регистру).

Пример:

Entry.objects.get(headline__contains='Lennon')

Эквивалент SQL:

SELECT ... WHERE headline LIKE '%Lennon%';

Обратите внимание, что это будет соответствовать заголовку 'Lennon honored today', но не 'lennon honored today'.

Пользователи SQLite

SQLite не поддерживает чувствительные к регистру операторы LIKE; contains действует как icontains для SQLite. См. примечание к базе данных для получения дополнительной информации.

Нечувствительное к регистру Содержит

Проверка наличия подстроки (нечувствительная к регистру).

Пример:

Entry.objects.get(headline__icontains='Lennon')

Эквивалент SQL:

SELECT ... WHERE headline ILIKE '%Lennon%';

Пользователи SQLite

При использовании бэкенда SQLite и строках, не содержащих символов ASCII, учтите примечание к базе данных относительно сравнения строк.

in

В заданном итерируемом объекте; часто это список, кортеж или набор результатов запроса. Это не часто используемый случай, но строки (так как они итерируемы) принимаются.

Примеры:

Entry.objects.filter(id__in=[1, 3, 4])
Entry.objects.filter(headline__in='abc')

Эквиваленты в SQL:

SELECT ... WHERE id IN (1, 3, 4);
SELECT ... WHERE headline IN ('a', 'b', 'c');

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

inner_qs = Blog.objects.filter(name__contains='Cheddar')
entries = Entry.objects.filter(blog__in=inner_qs)

Этот набор результатов запроса будет вычислен как подзапрос:

SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')

Если вы передаёте QuerySet, полученный из values() или values_list(), в качестве значения для __in поиска, убедитесь, что вы извлекаете только одно поле в результате. Например, это сработает (фильтрация по именам блогов):

inner_qs = Blog.objects.filter(name__contains='Ch').values('name')
entries = Entry.objects.filter(blog__name__in=inner_qs)

Этот пример вызовет исключение, так как внутренний запрос пытается извлечь два значения поля, а ожидается только одно:

# Bad code! Will raise a TypeError.
inner_qs = Blog.objects.filter(name__contains='Ch').values('name', 'id')
entries = Entry.objects.filter(blog__name__in=inner_qs)

Учитывая производительность

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

values = Blog.objects.filter(
        name__contains='Cheddar').values_list('pk', flat=True)
entries = Entry.objects.filter(blog__in=list(values))

Обратите внимание на вызов list() вокруг блогов QuerySet, чтобы заставить выполнить первый запрос. Без него будет выполнен вложенный запрос, потому что наборы результатов запросов — ленивые.

gt

Больше, чем.

Пример:

Entry.objects.filter(id__gt=4)

Эквивалент в SQL:

SELECT ... WHERE id > 4;

gte

Больше или равно.

lt

Меньше, чем.

lte

Меньше или равно.

startswith

Чувствительное к регистру начало с.

Пример:

Entry.objects.filter(headline__startswith='Lennon')

Эквивалент в SQL:

SELECT ... WHERE headline LIKE 'Lennon%';

SQLite не поддерживает операторы с чувствительностью к регистру; LIKE действует как istartswith для SQLite.

istartswith

Нечувствительное к регистру начало с.

Пример:

Entry.objects.filter(headline__istartswith='Lennon')

Эквивалент в SQL:

SELECT ... WHERE headline ILIKE 'Lennon%';

Пользователи SQLite

При использовании бэкенда SQLite и строк, отличных от ASCII, учтите примечание к базе данных о сравнении строк.

endswith

Чувствительное к регистру окончание с.

Пример:

Entry.objects.filter(headline__endswith='Lennon')

Эквивалент в SQL:

SELECT ... WHERE headline LIKE '%Lennon';

Пользователи SQLite

SQLite не поддерживает операторы с чувствительностью к регистру; LIKE действует как iendswith для SQLite. Для получения дополнительной информации обратитесь к документации примечания к базе данных.

iendswith

Нечувствительное к регистру окончание с.

Пример:

Entry.objects.filter(headline__iendswith='Lennon')

Эквивалент в SQL:

SELECT ... WHERE headline ILIKE '%Lennon'

Пользователи SQLite

При использовании бэкенда SQLite и строк, отличных от ASCII, учтите примечание к базе данных о сравнении строк.

range

Тест диапазона (включительно).

Пример:

import datetime
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)
Entry.objects.filter(pub_date__range=(start_date, end_date))

Эквивалент в SQL:

SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';

Вы можете использовать range везде, где вы можете использовать BETWEEN в SQL — для дат, чисел и даже символов.

Предупреждение

Фильтрация DateTimeField по датам не будет включать записи в последний день, потому что границы интерпретируются как «0:00 в указанную дату». Если pub_date был DateTimeField, вышеприведённое выражение преобразуется в следующее SQL:

SELECT ... WHERE pub_date BETWEEN '2005-01-01 00:00:00' and '2005-03-31 00:00:00';

Как правило, вы не можете смешивать даты и datetime.

date

Для полей datetime значение преобразуется в дату. Разрешает цепочку дополнительных поисков по полям. Принимает значение даты.

Пример:

Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

(Для этого поиска не включён фрагмент эквивалентного кода SQL, так как реализация соответствующего запроса различается в разных базах данных.)

Когда USE_TZ равно True, поля преобразуются в текущую временную зону перед фильтрацией. Это требует определений часовых поясов в базе данных.

year

Для полей date и datetime точное совпадение года. Разрешает цепочку дополнительных поисков по полям. Принимает целое число года.

Пример:

Entry.objects.filter(pub_date__year=2005)
Entry.objects.filter(pub_date__year__gte=2005)

Эквивалент в SQL:

SELECT ... WHERE pub_date BETWEEN '2005-01-01' AND '2005-12-31';
SELECT ... WHERE pub_date >= '2005-01-01';

(Точный синтаксис SQL варьируется в зависимости от базы данных.)

Когда USE_TZ равно True, поля datetime преобразуются в текущую временную зону перед фильтрацией. Это требует определений часовых поясов в базе данных.

iso_year

Для полей date и datetime точное совпадение года по ISO 8601. Разрешает цепочку дополнительных поисков по полям. Принимает целое число года.

Пример:

Entry.objects.filter(pub_date__iso_year=2005)
Entry.objects.filter(pub_date__iso_year__gte=2005)

(Точный синтаксис SQL варьируется в зависимости от базы данных.)

Когда USE_TZ равно True, поля datetime преобразуются в текущую временную зону перед фильтрацией. Это требует определений часовых поясов в базе данных.

month

Для полей date и datetime точное совпадение месяца. Разрешает цепочку дополнительных поисков по полям. Принимает целое число от 1 (январь) до 12 (декабрь).

Пример:

Entry.objects.filter(pub_date__month=12)
Entry.objects.filter(pub_date__month__gte=6)

Эквивалент в SQL:

SELECT ... WHERE EXTRACT('month' FROM pub_date) = '12';
SELECT ... WHERE EXTRACT('month' FROM pub_date) >= '6';

(Точный синтаксис SQL варьируется в зависимости от базы данных.)

Когда USE_TZ равно True, поля datetime преобразуются в текущую временную зону перед фильтрацией. Это требует определений часовых поясов в базе данных.

day

Для полей date и datetime точное совпадение дня. Разрешает цепочку дополнительных поисков по полям. Принимает целое число дня.

Пример:

Entry.objects.filter(pub_date__day=3)
Entry.objects.filter(pub_date__day__gte=3)

Эквивалент в SQL:

SELECT ... WHERE EXTRACT('day' FROM pub_date) = '3';
SELECT ... WHERE EXTRACT('day' FROM pub_date) >= '3';

(Точный синтаксис SQL варьируется в зависимости от базы данных.)

Это будет соответствовать любой записи с pub_date на 3-й день месяца, например, 3 января, 3 июля и т.д.

Когда USE_TZ равно True, поля datetime преобразуются в текущую временную зону перед фильтрацией. Это требует определений часовых поясов в базе данных.

week

Для полей date и datetime возвращает номер недели (от 1 до 52 или 53) в соответствии с ISO-8601, т.е. недели начинаются с понедельника, а первая неделя содержит четверг первого года.

Пример:

Entry.objects.filter(pub_date__week=52)
Entry.objects.filter(pub_date__week__gte=32, pub_date__week__lte=38)

(Для этого поиска не включён фрагмент эквивалентного кода SQL, так как реализация соответствующего запроса различается в разных базах данных.)

Когда USE_TZ равно True, поля datetime преобразуются в текущую временную зону перед фильтрацией. Это требует определений часовых поясов в базе данных.

week_day

Для полей date и datetime соответствие «дню недели». Разрешает цепочку дополнительных поисков по полям.

Принимает целое значение, представляющее день недели от 1 (воскресенье) до 7 (суббота).

Пример:

Entry.objects.filter(pub_date__week_day=2)
Entry.objects.filter(pub_date__week_day__gte=2)

(Для этого поиска не включён фрагмент эквивалентного кода SQL, так как реализация соответствующего запроса различается в разных базах данных.)

Это будет соответствовать любой записи с pub_date, которая выпадает на понедельник (день 2 недели), независимо от месяца или года, в котором это происходит. Дни недели индексируются с 1 (воскресенье) до 7 (суббота).

Когда USE_TZ равно True, поля datetime преобразуются в текущую временную зону перед фильтрацией. Это требует определений часовых поясов в базе данных.

iso_week_day

Для полей date и datetime точное соответствие дню недели по ISO 8601. Разрешает цепочку дополнительных поисков по полям.

Принимает целое значение, представляющее день недели от 1 (понедельник) до 7 (воскресенье).

Пример:

Entry.objects.filter(pub_date__iso_week_day=1)
Entry.objects.filter(pub_date__iso_week_day__gte=1)

(Для этого поиска не включён фрагмент эквивалентного кода SQL, так как реализация соответствующего запроса различается в разных базах данных.)

Это будет соответствовать любой записи с pub_date, которая выпадает на понедельник (день 1 недели), независимо от месяца или года, в котором это происходит. Дни недели индексируются с 1 (понедельник) до 7 (воскресенье).

Когда USE_TZ имеет значение True, поля datetime преобразуются в текущую временную зону перед фильтрацией. Это требует определения временных зон в базе данных.

quarter

Для полей date и datetime, поиск по «кварталу года». Позволяет цепочку дополнительных поисков по полям. Принимает целое число от 1 до 4, представляющее квартал года.

Пример получения записей во втором квартале (с 1 апреля по 30 июня):

Entry.objects.filter(pub_date__quarter=2)

(В данном случае фрагмент SQL-кода не включен, так как реализация соответствующего запроса варьируется в зависимости от СУБД.)

Когда USE_TZ имеет значение True, поля datetime преобразуются в текущую временную зону перед фильтрацией. Это требует определения временных зон в базе данных.

time

Для полей datetime, приведение значения к типу time. Позволяет цепочку дополнительных поисков по полям. Принимает значение типа datetime.time.

Пример:

Entry.objects.filter(pub_date__time=datetime.time(14, 30))
Entry.objects.filter(pub_date__time__range=(datetime.time(8), datetime.time(17)))

(В данном случае фрагмент SQL-кода не включен, так как реализация соответствующего запроса варьируется в зависимости от СУБД.)

Когда USE_TZ имеет значение True, поля преобразуются в текущую временную зону перед фильтрацией. Это требует определения временных зон в базе данных.

hour

Для полей datetime и time, точное совпадение часа. Позволяет цепочку дополнительных поисков по полям. Принимает целое число от 0 до 23.

Пример:

Event.objects.filter(timestamp__hour=23)
Event.objects.filter(time__hour=5)
Event.objects.filter(timestamp__hour__gte=12)

Эквивалент на SQL:

SELECT ... WHERE EXTRACT('hour' FROM timestamp) = '23';
SELECT ... WHERE EXTRACT('hour' FROM time) = '5';
SELECT ... WHERE EXTRACT('hour' FROM timestamp) >= '12';

(Точный синтаксис SQL зависит от каждой СУБД.)

Когда USE_TZ имеет значение True, поля datetime преобразуются в текущую временную зону перед фильтрацией. Это требует определения временных зон в базе данных.

minute

Для полей datetime и time, точное совпадение минуты. Позволяет цепочку дополнительных поисков по полям. Принимает целое число от 0 до 59.

Пример:

Event.objects.filter(timestamp__minute=29)
Event.objects.filter(time__minute=46)
Event.objects.filter(timestamp__minute__gte=29)

Эквивалент на SQL:

SELECT ... WHERE EXTRACT('minute' FROM timestamp) = '29';
SELECT ... WHERE EXTRACT('minute' FROM time) = '46';
SELECT ... WHERE EXTRACT('minute' FROM timestamp) >= '29';

(Точный синтаксис SQL зависит от каждой СУБД.)

Когда USE_TZ имеет значение True, поля datetime преобразуются в текущую временную зону перед фильтрацией. Это требует определения временных зон в базе данных.

second

Для полей datetime и time, точное совпадение секунды. Позволяет цепочку дополнительных поисков по полям. Принимает целое число от 0 до 59.

Пример:

Event.objects.filter(timestamp__second=31)
Event.objects.filter(time__second=2)
Event.objects.filter(timestamp__second__gte=31)

Эквивалент на SQL:

SELECT ... WHERE EXTRACT('second' FROM timestamp) = '31';
SELECT ... WHERE EXTRACT('second' FROM time) = '2';
SELECT ... WHERE EXTRACT('second' FROM timestamp) >= '31';

(Точный синтаксис SQL зависит от каждой СУБД.)

Когда USE_TZ имеет значение True, поля datetime преобразуются в текущую временную зону перед фильтрацией. Это требует определения временных зон в базе данных.

isnull

Принимает либо True, либо False, которые соответствуют SQL-запросам IS NULL и IS NOT NULL соответственно.

Пример:

Entry.objects.filter(pub_date__isnull=True)

Эквивалент на SQL:

SELECT ... WHERE pub_date IS NULL;

regex

Поиск по регулярному выражению (чувствительно к регистру).

Синтаксис регулярного выражения соответствует синтаксису используемой СУБД. В случае SQLite, который не поддерживает встроенные регулярные выражения, эта функция обеспечивается пользовательской функцией REGEXP (на Python), и синтаксис регулярного выражения поэтому соответствует синтаксису модуля re Python.

Пример:

Entry.objects.get(title__regex=r'^(An?|The) +')

Эквиваленты на SQL:

SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL

SELECT ... WHERE REGEXP_LIKE(title, '^(An?|The) +', 'c'); -- Oracle

SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL

SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite

Рекомендуется использовать сырые строки (например, r'foo' вместо 'foo') для передачи синтаксиса регулярного выражения.

iregex

Поиск по регулярному выражению (нечувствительно к регистру).

Пример:

Entry.objects.get(title__iregex=r'^(an?|the) +')

Эквиваленты на SQL:

SELECT ... WHERE title REGEXP '^(an?|the) +'; -- MySQL

SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'i'); -- Oracle

SELECT ... WHERE title ~* '^(an?|the) +'; -- PostgreSQL

SELECT ... WHERE title REGEXP '(?i)^(an?|the) +'; -- SQLite

Функции агрегации

Django предоставляет следующие функции агрегации в модуле django.db.models. Подробности использования этих функций агрегации см. в руководстве по агрегации. Ознакомьтесь с документацией по Aggregate, чтобы узнать, как создавать свои агрегаты.

Предупреждение

SQLite не поддерживает агрегацию по полям date/time без дополнительных настроек. Это связано с тем, что в SQLite нет встроенных полей date/time, а Django в настоящий момент эмулирует эти функции с использованием текстового поля. Попытки использования агрегации по полям date/time в SQLite приведут к ошибке NotSupportedError.

Примечание

Функции агрегации возвращают значение None, когда используются с пустым набором QuerySet. Например, функция агрегации Sum возвращает None, а не 0, если набор QuerySet не содержит записей. Чтобы вернуть другое значение вместо этого, передайте значение аргументу default. Исключением является функция Count, которая возвращает значение 0, если набор QuerySet пуст. Count не поддерживает аргумент default.

Все агрегаты имеют общие параметры:

expressions

Строки, которые ссылаются на поля модели, преобразования полей или выражения запроса.

Изменено в Django 3.2:

Добавлена поддержка преобразований полей.

output_field

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

Примечание

При объединении нескольких типов полей Django может определить только output_field, если все поля одного типа. В противном случае вы должны указать output_field самостоятельно.

filter

Необязательный объект Q object, используемый для фильтрации строк, которые агрегируются.

См. Условную агрегацию и Фильтрацию по аннотациям для примеров использования.

default

Добавлена в Django 4.0.

Необязательный аргумент, который позволяет указать значение по умолчанию, используемое при отсутствии записей в наборе запросов (или группировке).

**extra

Ключевые аргументы, которые могут предоставить дополнительный контекст для SQL, сгенерированного агрегатом.

Avg

class Avg(expression, output_field=None, distinct=False, filter=None, default=None, **extra)

Возвращает среднее значение заданного выражения, которое должно быть числовым, если не указано иное output_field.

  • Стандартное имя: <field>__avg
  • Тип возвращаемого значения: float, если входной параметр int, в противном случае, такой же тип, как у входного поля, или output_field, если задан

Имеет один необязательный аргумент:

distinct

Если distinct=True, Avg возвращает среднее значение уникальных значений. Это эквивалент SQL-запроса AVG(DISTINCT <field>). Значение по умолчанию — False.

Count

class Count(expression, distinct=False, filter=None, **extra)

Возвращает количество объектов, связанных через предоставленное выражение.

  • Стандартное имя: <field>__count
  • Тип возвращаемого значения: int

Имеет один необязательный аргумент:

distinct

Если distinct=True, подсчёт будет включать только уникальные экземпляры. Это эквивалент SQL-запроса COUNT(DISTINCT <field>). Значение по умолчанию — False.

Примечание

Аргумент default не поддерживается.

Max

class Max(expression, output_field=None, filter=None, default=None, **extra)

Возвращает максимальное значение заданного выражения.

  • Стандартное имя: <field>__max
  • Тип возвращаемого значения: такой же, как тип входного поля, или output_field, если задан

Min

class Min(expression, output_field=None, filter=None, default=None, **extra)

Возвращает минимальное значение заданного выражения.

  • Стандартное имя: <field>__min
  • Тип возвращаемого значения: такой же, как тип входного поля, или output_field, если задан

StdDev

class StdDev(expression, output_field=None, sample=False, filter=None, default=None, **extra)

Возвращает стандартное отклонение данных в предоставленном выражении.

  • По умолчанию псевдоним: <field>__stddev
  • Тип возвращаемого значения: float, если входной параметр int, в противном случае — такой же, как у входного поля, или output_field, если предоставлен

Имеет один необязательный аргумент:

sample

По умолчанию, StdDev возвращает стандартное отклонение генеральной совокупности. Однако, если sample=True, возвращаемое значение будет стандартным отклонением выборки.

Sum

class Sum(expression, output_field=None, distinct=False, filter=None, default=None, **extra)

Вычисляет сумму всех значений данного выражения.

  • По умолчанию псевдоним: <field>__sum
  • Тип возвращаемого значения: такой же, как у входного поля, или output_field, если предоставлен

Имеет один необязательный аргумент:

distinct

Если distinct=True, Sum возвращает сумму уникальных значений. Это эквивалент SQL запросу SUM(DISTINCT <field>). Значение по умолчанию — False.

Variance

class Variance(expression, output_field=None, sample=False, filter=None, default=None, **extra)

Возвращает дисперсию данных в предоставленном выражении.

  • По умолчанию псевдоним: <field>__variance
  • Тип возвращаемого значения: float, если входной параметр int, в противном случае — такой же, как у входного поля, или output_field, если предоставлен

Имеет один необязательный аргумент:

sample

По умолчанию, Variance возвращает дисперсию генеральной совокупности. Однако, если sample=True, возвращаемое значение будет дисперсией выборки.

Инструменты, связанные с запросами

Этот раздел содержит справочную информацию по инструментам, связанным с запросами, которые не документированы в других местах.

Q() объекты

class Q

Объект Q() представляет собой SQL условие, которое может использоваться в операциях, связанных с базой данных. Он похож на то, как объект F() представляет значение поля модели или аннотации. Они позволяют определять и повторно использовать условия, а также комбинировать их с помощью операторов, таких как | (OR) и & (AND). См. Сложные запросы с объектами Q.

Prefetch() объекты

class Prefetch(lookup, queryset=None, to_attr=None)

Объект Prefetch() может использоваться для управления операциями prefetch_related().

Аргумент lookup описывает отношения для следования и работает аналогично строковым выражениям, передаваемым в prefetch_related(). Например:

>>> from django.db.models import Prefetch
>>> Question.objects.prefetch_related(Prefetch('choice_set')).get().choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
# This will only execute two queries regardless of the number of Question
# and Choice objects.
>>> Question.objects.prefetch_related(Prefetch('choice_set')).all()
<QuerySet [<Question: What's up?>]>

Аргумент queryset предоставляет базовый QuerySet для данного запроса. Это полезно для дальнейшего уточнения операции префетча или вызова select_related() из связанного отношения, тем самым ещё больше уменьшая количество запросов:

>>> voted_choices = Choice.objects.filter(votes__gt=0)
>>> voted_choices
<QuerySet [<Choice: The sky>]>
>>> prefetch = Prefetch('choice_set', queryset=voted_choices)
>>> Question.objects.prefetch_related(prefetch).get().choice_set.all()
<QuerySet [<Choice: The sky>]>

Аргумент to_attr устанавливает результат операции префетча в пользовательское свойство:

>>> prefetch = Prefetch('choice_set', queryset=voted_choices, to_attr='voted_choices')
>>> Question.objects.prefetch_related(prefetch).get().voted_choices
[<Choice: The sky>]
>>> Question.objects.prefetch_related(prefetch).get().choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

Примечание

При использовании to_attr результат префетча хранится в списке. Это может значительно улучшить производительность по сравнению с традиционными вызовами prefetch_related, которые хранят кэшированный результат внутри экземпляра QuerySet.

prefetch_related_objects()

prefetch_related_objects(model_instances, *related_lookups)

Выполняет префетч заданных запросов по итерируемому списку экземпляров моделей. Это полезно в коде, который получает список экземпляров моделей, в отличие от QuerySet; например, при извлечении моделей из кэша или их ручном создании.

Передайте итерируемый список экземпляров моделей (все должны быть одного класса) и запросы или объекты Prefetch, которые вы хотите префечить. Например:

>>> from django.db.models import prefetch_related_objects
>>> restaurants = fetch_top_restaurants_from_cache()  # A list of Restaurants
>>> prefetch_related_objects(restaurants, 'pizzas__toppings')

При использовании нескольких баз данных с prefetch_related_objects запрос префетча будет использовать базу данных, связанную с экземпляром модели. Это можно переопределить, используя пользовательский набор запросов в связанном запросе.

FilteredRelation() объекты

class FilteredRelation(relation_name, *, condition=Q())
relation_name

Имя поля, по которому вы хотите отфильтровать отношение.

condition

Объект Q для управления фильтрацией.

FilteredRelation используется с annotate() для создания ON условия, когда выполняется JOIN. Она не действует на стандартное отношение, а на имя аннотации (pizzas_vegetarian в примере ниже).

Например, чтобы найти рестораны, у которых есть вегетарианские пиццы с 'mozzarella' в названии:

>>> from django.db.models import FilteredRelation, Q
>>> Restaurant.objects.annotate(
...    pizzas_vegetarian=FilteredRelation(
...        'pizzas', condition=Q(pizzas__vegetarian=True),
...    ),
... ).filter(pizzas_vegetarian__name__icontains='mozzarella')

Если пицц много, этот запрос работает лучше, чем:

>>> Restaurant.objects.filter(
...     pizzas__vegetarian=True,
...     pizzas__name__icontains='mozzarella',
... )

потому что фильтрация в WHERE части первого запроса будет работать только с вегетарианскими пиццами.

FilteredRelation не поддерживает:

  • QuerySet.only() и prefetch_related().
  • Поле GenericForeignKey, унаследованное от родительской модели.
Изменено в Django 3.2:

Добавлена поддержка вложенных отношений.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/4.0/ref/models/querysets/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API