Spec-Zone.ru › Django 3.2

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

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

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

Когда 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, он содержит результаты на момент сериализации, а не результаты, которые в настоящее время находятся в базе данных.

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

Поскольку ошибки совместимости сериализованных данных могут быть трудно диагностируемы (например, объекты могут быть скрытно повреждены), при попытке десериализации QuerySet в версии 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(**kwargs)

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

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

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

exclude()

exclude(**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.

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('?')

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

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

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 , если запрос был отсортирован любым способом.

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

Entry.objects.order_by('headline').order_by('pub_date')

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

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

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

reverse()

reverse()
END_OF_DOCUMENT_MARKER

Используйте метод 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. Это приводит к SELECT DISTINCT ON SQL-запросу. Вот разница. При обычном вызове 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(), в этом случае будут возвращены все возможные комбинации.

Значения булевых типов для JSONField в SQLite

Из-за способа реализации функции JSON_EXTRACT SQL в SQLite, values() вернут 1 и 0 вместо True и False для преобразований ключей JSONField.

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,)]>

Значения булевых типов для JSONField в SQLite

Из-за способа реализации функции JSON_EXTRACT SQL в SQLite, values_list() вернет 1 и 0 вместо True и False для преобразований ключей JSONField.

dates()

dates(field, kind, order='ASC')

Возвращает QuerySet, оцениваемый как список объектов datetime.date, представляющих все доступные даты определенного типа в содержимом QuerySet.

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

  • "year" возвращает список всех уникальных значений года для поля.
  • "month" возвращает список всех уникальных значений год/месяц для поля.
  • "week" возвращает список всех уникальных значений год/неделя для поля. Все даты будут понедельниками.
  • "day" возвращает список всех уникальных значений год/месяц/день для поля.

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

Примеры:

>>> Entry.objects.dates('pub_date', 'year')
[datetime.date(2005, 1, 1)]
>>> Entry.objects.dates('pub_date', 'month')
[datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)]
>>> Entry.objects.dates('pub_date', 'week')
[datetime.date(2005, 2, 14), datetime.date(2005, 3, 14)]
>>> Entry.objects.dates('pub_date', 'day')
[datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)]
>>> Entry.objects.dates('pub_date', 'day', order='DESC')
[datetime.date(2005, 3, 20), datetime.date(2005, 2, 20)]
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
[datetime.date(2005, 3, 20)]

datetimes()

datetimes(field_name, kind, order='ASC', tzinfo=None, is_dst=None)

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

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

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

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

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

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

Введено в Django 3.1:

Добавлен параметр is_dst.

Примечание

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

  • SQLite: нет требований. Преобразования выполняются в Python с помощью pytz (установлен при установке Django).
  • 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, даже если аргументы являются экземплярами других моделей. Передача различных моделей работает, если список SELECT одинаков во всех QuerySet (по крайней мере, типы, имена не важны, пока типы находятся в том же порядке). В таких случаях необходимо использовать имена столбцов из первой QuerySet в методах QuerySet применяемых к результирующему QuerySet. Например:

>>> qs1 = Author.objects.values_list('name')
>>> qs2 = Entry.objects.values_list('headline')
>>> qs1.union(qs2).order_by('name')

Кроме того, только LIMIT, OFFSET, COUNT(*), ORDER BY, и указание столбцов (т.е. срезы, count(), exists(), order_by() и values()/values_list()) разрешены для результирующего QuerySet. Кроме того, базы данных накладывают ограничения на разрешенные операции в объединённых запросах. Например, большинство баз данных не допускают LIMIT или OFFSET в объединённых запросах.

intersection()

intersection(*other_qs)

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

>>> qs1.intersection(qs2, qs3)

См. union() для некоторых ограничений.

difference()

difference(*other_qs)

Использует оператор SQL EXCEPT для сохранения только элементов, присутствующих в QuerySet, но отсутствующих в других QuerySet. Например:

>>> qs1.difference(qs2, qs3)

См. union() для некоторых ограничений.

select_related()

select_related(*fields)

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

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

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

# Hits the database again to get the related Blog object.
b = e.blog

А вот select_related запрос:

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

# Doesn't hit the database, because e.blog has been prepopulated
# in the previous query.
b = e.blog

Вы можете использовать select_related() с любым набором объектов:

from django.utils import timezone

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

for e in Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog'):
    # Without select_related(), this would make a database query for each
    # loop iteration in order to fetch the related blog for each entry.
    blogs.add(e.blog)

Порядок цепочки filter() и select_related() не важен. Эти наборы запросов эквивалентны:

Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog')
Entry.objects.select_related('blog').filter(pub_date__gt=timezone.now())

Вы можете следовать за внешними ключами аналогично их запросу. Если у вас есть следующие модели:

from django.db import models

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

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

class Book(models.Model):
    # ...
    author = models.ForeignKey(Person, on_delete=models.CASCADE)

… тогда вызов Book.objects.select_related('author__hometown').get(id=4) кэширует связанные Person и связанные City:

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

# Without select_related()...
b = Book.objects.get(id=4)  # Hits the database.
p = b.author         # Hits the database.
c = p.hometown       # Hits the database.

Вы можете ссылаться на любую связь ForeignKey или OneToOneField в списке полей, переданных в select_related().

Вы также можете сослаться на обратное направление связи OneToOneField в списке полей, переданных в select_related — то есть вы можете пройти по связи OneToOneField обратно к объекту, в котором определено поле. Вместо указания имени поля используйте related_name для поля в связанном объекте.

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

Если вам нужно очистить список связанных полей, добавленных предыдущими вызовами 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, которые обычно пытаются избежать загрузки всех объектов в память до их необходимости, даже после выполнения запроса в базе данных.

END_OF_DOCUMENT_MARKER

Примечание

Помните, что, как всегда в случае с 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 может привести к неожиданным результатам при смешивании типов. Если вы запрашиваете столбец типа string, но с целочисленным значением, 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'))

Только в PostgreSQL вы можете передать no_key=True для получения более слабой блокировки, которая всё ещё позволяет создавать строки, которые просто ссылаются на заблокированные строки (например, через внешний ключ), пока блокировка активна. В документации PostgreSQL более подробно описаны режимы блокировок уровня строк https://www.postgresql.org/docs/current/explicit-locking.html#LOCKING-ROWS.

Вы не можете использовать select_for_update() для nullable отношений:

>>> 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

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

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

В настоящее время базы данных postgresql, oracle, и mysql поддерживают select_for_update(). Однако MariaDB 10.3+ поддерживает только аргумент nowait, а 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() в режиме autocommit в бэкендах, поддерживающих SELECT ... FOR UPDATE, является ошибкой TransactionManagementError, потому что в этом случае строки не блокируются. Если это будет разрешено, это облегчит повреждение данных и может быть легко вызвано кодом, который ожидает выполнения вне транзакции.

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

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

Хотя select_for_update() обычно завершается ошибкой в режиме autocommit, поскольку 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+.

raw()

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

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

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

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

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

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

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

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

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

И (&)

Объединяет два набора запросов с помощью оператора 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

ИЛИ (|)

Объединяет два набора запросов с помощью оператора 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 и возвращают что-то кроме набора запросов.

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

get()

get(**kwargs)

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

Entry.objects.get(id=1)
Entry.objects.get(blog=blog, 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() через поле главы книги, но это извлекает информацию только в контексте этой книги:

>>> 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+). На других базах данных он не будет установлен.
  • Он не работает с множественными связями многие-ко-многим.
  • Он преобразует 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 3.1:

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

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'])

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().

in_bulk()

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

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

Пример:

>>> 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) по результатам. QuerySet обычно кеширует свои результаты, чтобы повторные вычисления не приводили к дополнительным запросам. В отличие от iterator(), которое считывает результаты напрямую, без кэширования на уровне QuerySet (внутренне, итератор по умолчанию вызывает iterator() и кеширует возвращаемое значение). Для QuerySet, который возвращает большое количество объектов, которые вам нужно получить только один раз, это может повысить производительность и значительно сократить потребление памяти.

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

Также использование 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() могут вернуть экземпляры с нулевыми датами.

Поскольку сортировка делегируется базе данных, результаты по полям, допускающим нулевые значения, могут быть отсортированы по-разному, если вы используете разные базы данных. Например, PostgreSQL и MySQL сортируют нулевые значения так, как будто они выше, чем ненулевые, в то время как 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, особенно в контексте большого QuerySet.

Наиболее эффективным методом определения, является ли модель с уникальным полем (например, primary_key) членом QuerySet, является:

entry = Entry.objects.get(pk=123)
if some_queryset.filter(pk=entry.pk).exists():
    print("Entry contained in queryset")

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

if entry in some_queryset:
   print("Entry contained in 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), которое извлекает результаты, а затем проверяет, были ли возвращены какие-либо.

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()

delete()

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

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

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

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

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

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

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

# This will delete all Blogs and all of their Entry objects.
>>> blogs.delete()
(5, {'weblog.Blog': 1, 'weblog.Entry': 2, 'weblog.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.

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

Добавлена поддержка формата 'TREE' в MySQL 8.0.16+ и опции analyze в MariaDB и MySQL 8.0.18+.

Field поиски

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

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

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

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

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 таблицы на использование чувствительных к регистру сравнений, но это влечёт определённые компромиссы. Для получения дополнительной информации см. раздел «кодировка» в документации по кодировке баз данных.

iexact

Нечувствительное к регистру точное совпадение. Если предоставленное значение для сравнения — 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 строк.

contains

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

Пример:

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

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

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

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

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

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

icontains

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

Пример:

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() вокруг Blog QuerySet , чтобы принудительно выполнить первый запрос. Без него был бы выполнен вложенный запрос, так как QuerySets являются ленивыми.

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 , чувствительные к регистру; startswith действует как 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 , чувствительные к регистру; endswith действует как 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 везде, где в SQL можно использовать BETWEEN — для дат, чисел и даже символов.

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

Фильтрация 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';

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

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 июля и т.д.

Когда 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

Новое в Django 3.1.

Для полей типа 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 значение преобразуется во время. Позволяет объединять дополнительные проверки полей. Принимает значение 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;

Устарело начиная с версии 3.1: Использование значений, отличных от булевых, в правой части устарело, используйте True или False вместо этого. В Django 4.0 будет поднято исключение.

regex

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

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

Пример:

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 не может обрабатывать агрегирование по полям даты/времени без дополнительных настроек. Это связано с тем, что в SQLite нет встроенных полей даты/времени, и Django в настоящее время эмулирует эти функции с помощью текстового поля. Попытки использовать агрегирование по полям даты/времени в SQLite приведут к исключению NotSupportedError.

Примечание

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

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

expressions

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

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

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

output_field

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

Примечание

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

filter

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

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

**extra

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

Avg

class Avg(expression, output_field=None, distinct=False, filter=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.

Max

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

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

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

Min

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

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

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

StdDev

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

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

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

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

sample

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

Sum

class Sum(expression, output_field=None, distinct=False, filter=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, **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().
  • A GenericForeignKey унаследованный от родительской модели.
Изменено в Django 3.2:

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

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

Spec-Zone.ru

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