Spec-Zone.ru › Django 6.0

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

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

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

Когда вычисляются QuerySetы

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

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

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

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

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

  • Асинхронная итерация. По QuerySet также можно выполнять итерацию с помощью async for:

    async for e in Entry.objects.all():
        results.append(e)
    

    Синхронные и асинхронные итераторы QuerySet используют общий кэш.

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

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

  • Сериализация/кэширование. Подробнее о том, что происходит при сериализации QuerySet, см. в следующем разделе. Для целей этого раздела важно, что результаты считываются из базы данных.
  • repr(). QuerySet вычисляется при вызове для него repr(). Это сделано для удобства работы в интерактивном интерпретаторе Python: при интерактивном использовании API результаты отображаются сразу.
  • len(). QuerySet вычисляется при вызове для него len(). Как и следовало ожидать, эта функция возвращает длину списка результатов.

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

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

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

    if Entry.objects.filter(headline="Test"):
        print("There is at least one Entry with the headline Test")
    

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

Сериализация QuerySetов

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

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

>>> import pickle
>>> query = pickle.loads(s)  # Assuming 's' is the pickled string.
>>> qs = MyModel.objects.all()
>>> qs.query = query  # Restore the original 'query'.

Атрибут query — это непрозрачный объект. Он содержит внутренние данные построения запроса и не является частью публичного API. Однако безопасно сериализовать и десериализовать содержимое этого атрибута описанным здесь способом; такая операция полностью поддерживается.

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

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

>>> import pickle
>>> qs = Blog.objects.values_list("id", "name")
>>> qs
<QuerySet [(1, 'Beatles Blog')]>
>>> reloaded_qs = Blog.objects.all()
>>> reloaded_qs.query = pickle.loads(pickle.dumps(qs.query))
>>> reloaded_qs
<QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>

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

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

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

QuerySet API

Формальное объявление QuerySet:

class QuerySet(model=None, query=None, using=None, hints=None) [исходный код]

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

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

ordered [исходный код]

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

db [исходный код]

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

Примечание

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

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

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

Примечание

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

filter()

filter(*args, **kwargs)

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

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

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

exclude()

exclude(*args, **kwargs)

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

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

В этом примере исключаются все записи, у которых pub_date позже 2005-1-3 И headline равно «Hello»:

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline="Hello")

В терминах SQL это вычисляется так:

SELECT ...
WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')

В этом примере исключаются все записи, у которых pub_date позже 2005-1-3 ИЛИ заголовок равен «Hello»:

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline="Hello")

В терминах SQL это вычисляется так:

SELECT ...
WHERE NOT pub_date > '2005-1-3'
AND NOT headline = 'Hello'

Обратите внимание, что второй пример задаёт более строгие условия.

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

annotate()

annotate(*args, **kwargs)

Добавляет каждому объекту в QuerySet аннотации из переданного списка выражений запроса или объектов Q. Каждому объекту можно добавить аннотацию в виде:

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

Работает так же, как annotate(), но вместо добавления аннотаций к объектам в QuerySet сохраняет выражение для повторного использования другими методами QuerySet. Это полезно, когда результат самого выражения не нужен, но оно используется для фильтрации, сортировки или как часть сложного выражения. Отказ от выборки неиспользуемого значения устраняет избыточную работу базы данных, что должно повысить производительность.

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

>>> from django.db.models import Count
>>> blogs = Blog.objects.alias(entries=Count("entry")).filter(entries__gt=5)

alias() можно использовать вместе с annotate(), exclude(), filter(), order_by() и update(). Чтобы использовать выражение с псевдонимом в других методах (например, aggregate()), необходимо преобразовать его в аннотацию:

Blog.objects.alias(entries=Count("entry")).annotate(
    entries=F("entries"),
).aggregate(Sum("entries"))

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

order_by()

order_by(*fields)

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

Пример:

Entry.objects.filter(pub_date__year=2005).order_by("-pub_date", "headline")

Приведённый выше результат будет отсортирован по убыванию pub_date, а затем по возрастанию headline. Знак минус перед "-pub_date" указывает на сортировку по убыванию. Сортировка по возрастанию подразумевается по умолчанию. Для сортировки в случайном порядке используйте "?", например:

Entry.objects.order_by("?")

Примечание: запросы order_by('?') могут быть затратными и медленными в зависимости от используемой СУБД.

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

Entry.objects.order_by("blog__name", "headline")

Если попытаться отсортировать по полю, являющемуся связью с другой моделью, Django использует порядок сортировки по умолчанию связанной модели или первичный ключ связанной модели, если для неё не задан параметр Meta.ordering. Например, поскольку для модели Blog порядок сортировки по умолчанию не задан:

Entry.objects.order_by("blog")

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

Entry.objects.order_by("blog__id")

Если бы для Blog был задан ordering = ['name'], первый набор запросов был бы эквивалентен:

Entry.objects.order_by("blog__name")

Также можно сортировать по выражениям запроса, вызвав для выражения asc() или desc():

Entry.objects.order_by(Coalesce("summary", "headline").desc())

asc() и desc() принимают аргументы (nulls_first и nulls_last), управляющие сортировкой значений NULL.

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

Примечание

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

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

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


Event.objects.order_by("children__date")

Здесь для каждого Event потенциально может быть несколько значений сортировки; каждый Event, связанный с несколькими children, будет возвращён несколько раз в новом QuerySet, создаваемом order_by(). Иными словами, использование order_by() для QuerySet может вернуть больше объектов, чем было изначально, — что, вероятно, не ожидается и не приносит пользы.

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

Нельзя указать, должна ли сортировка учитывать регистр. Django сортирует результаты с учётом регистра так, как это обычно делает используемая СУБД.

Для сортировки без учёта регистра можно преобразовать поле в нижний регистр с помощью Lower:

Entry.objects.order_by(Lower("headline").desc())

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

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

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

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

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

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

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

reverse()

reverse()

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

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

my_queryset.reverse()[:5]

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

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

distinct()

distinct(*fields)

Возвращает новый QuerySet, в SQL-запросе которого используется SELECT DISTINCT. Это удаляет из результатов запроса повторяющиеся строки.

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

Примечание

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

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

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

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

Примечание

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

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

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

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

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

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

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

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

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

Примечание

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

Entry.objects.order_by("blog").distinct("blog")

…не сработает, поскольку запрос будет отсортирован по blog__name, что не соответствует выражению DISTINCT ON. Чтобы оба выражения совпадали, необходимо явно указать сортировку по полю связи _id (в данном случае blog_id) или по связанному полю (blog__pk).

values()

values(*fields, **expressions)

Возвращает QuerySet, который при переборе возвращает словари, а не экземпляры моделей.

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

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

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

# This list contains a dictionary.
>>> Blog.objects.filter(name__startswith="Beatles").values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>

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

Пример:

>>> Blog.objects.values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
>>> Blog.objects.values("id", "name")
<QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>

Метод values() также принимает необязательные именованные аргументы **expressions, которые передаются в annotate():

>>> from django.db.models.functions import Lower
>>> Blog.objects.values(lower_name=Lower("name"))
<QuerySet [{'lower_name': 'beatles blog'}]>

Для сортировки можно использовать встроенные и пользовательские способы поиска. Например:

>>> from django.db.models import CharField
>>> from django.db.models.functions import Lower
>>> CharField.register_lookup(Lower)
>>> Blog.objects.values("name__lower")
<QuerySet [{'name__lower': 'beatles blog'}]>

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

>>> from django.db.models import Count
>>> Blog.objects.values("entry__authors", entries=Count("entry"))
<QuerySet [{'entry__authors': 1, 'entries': 20}, {'entry__authors': 1, 'entries': 13}]>
>>> Blog.objects.values("entry__authors").annotate(entries=Count("entry"))
<QuerySet [{'entry__authors': 1, 'entries': 33}]>

Стоит упомянуть несколько нюансов:

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

    Например:

    >>> Entry.objects.values()
    <QuerySet [{'blog_id': 1, 'headline': 'First Entry', ...}, ...]>
    
    >>> Entry.objects.values("blog")
    <QuerySet [{'blog': 1}, ...]>
    
    >>> Entry.objects.values("blog_id")
    <QuerySet [{'blog_id': 1}, ...]>
    
  • При совместном использовании values() и distinct() учитывайте, что сортировка может повлиять на результаты. Подробности см. в примечании к distinct().
  • Если после вызова extra() использовать предложение values(), поля, заданные аргументом 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}]>
    

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

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

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

Из-за особенностей реализации функций SQL JSON_EXTRACT и JSON_TYPE в SQLite и отсутствия типа данных BOOLEAN для преобразований ключей JSONField values() возвращает True, False и None вместо строк "true", "false" и "null".

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

Предложение SELECT, формируемое при использовании values(), было обновлено, чтобы учитывать порядок заданных *fields и **expressions.

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

Из-за особенностей реализации SQL-функций JSON_EXTRACT и JSON_TYPE в SQLite, а также отсутствия типа данных BOOLEAN, для преобразований ключей JSONField метод values_list() возвращает True, False и None вместо строк "true", "false" и "null".

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

Оператор SELECT, генерируемый при использовании values_list(), был обновлён: теперь он учитывает порядок указанных *fields.

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)

Возвращает 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.

Примечание

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

  • SQLite: требований нет. Преобразования выполняются в Python.
  • PostgreSQL: требований нет (см. раздел Часовые пояса).
  • Oracle: требований нет (см. раздел Выбор файла часовых поясов).
  • MySQL: загрузите таблицы часовых поясов с помощью mysql_tzinfo_to_sql.

none()

none()

Вызов none() создаёт queryset, который никогда не возвращает объекты; при обращении к результатам запрос не выполняется. qs.none() queryset — это экземпляр EmptyQuerySet.

Примеры:

>>> Entry.objects.none()
<QuerySet []>
>>> from django.db.models.query import EmptyQuerySet
>>> isinstance(Entry.objects.none(), EmptyQuerySet)
True

all()

all()

Возвращает копию текущего QuerySet (или подкласса QuerySet). Это может быть полезно, когда требуется передать менеджер модели или QuerySet и выполнить дополнительную фильтрацию результата. После вызова all() для любого из этих объектов у вас гарантированно будет QuerySet, с которым можно работать.

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

union()

union(*other_qs, all=False)

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

>>> qs1.union(qs2, qs3)

По умолчанию оператор UNION выбирает только уникальные значения. Чтобы разрешить дубликаты, используйте аргумент all=True.

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

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

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

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() не имеет значения. Эти querysets эквивалентны:

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.

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

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

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

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

>>> without_relations = queryset.select_related(None)

Последовательные вызовы select_related работают так же, как и другие методы: select_related('foo', 'bar') эквивалентно select_related('foo').select_related('bar').

prefetch_related()

prefetch_related(*lookups)

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

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

select_related создаёт SQL-соединение и включает поля связанного объекта в оператор SELECT. Поэтому select_related получает связанные объекты в том же запросе к базе данных. Однако, чтобы избежать слишком большого набора результатов, который получился бы при соединении по связи «многие», select_related применяется только к связям с одним значением — внешним ключам и связям один-к-одному.

В свою очередь, prefetch_related выполняет отдельный запрос для каждой связи и объединяет результаты в Python. Это позволяет предварительно загружать объекты many-to-many, many-to-one и GenericRelation, что невозможно с помощью select_related, а также связи внешнего ключа и один-к-одному, поддерживаемые select_related. Метод также поддерживает предварительную загрузку GenericForeignKey, однако queryset для каждого ContentType необходимо передать в параметре querysets объекта GenericPrefetch.

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

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 Pizza QuerySet.

С помощью prefetch_related можно сократить количество запросов до двух:

>>> Pizza.objects.prefetch_related("toppings")

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

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

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

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

>>> Pizza.objects.prefetch_related("toppings")
#  "Hawaiian" Pizza was deleted in another shell.
<QuerySet [<Pizza: Hawaiian ()>, <Pizza: Seafood (prawns, smoked salmon)>]>

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

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

Примечание

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

>>> 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(), create(), 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")

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

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

Будут получены лучшая пицца и все её начинки для каждого ресторана. Для этого будут выполнены три запроса к базе данных: один для ресторанов, один для «лучших пицц» и один для начинок.

Связь best_pizza также можно получить с помощью select_related, сократив количество запросов до двух:

>>> 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() будут учитываться только в том случае, если задано значение chunk_size.

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

В простейшем случае Prefetch эквивалентен традиционному синтаксису путей в виде строк:

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

С помощью необязательного аргумента queryset можно передать пользовательский queryset. Это позволяет изменить порядок сортировки 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 из-за попытки переопределить queryset для уже обработанного пути. Обратите внимание: для перехода по 'pizzas' в рамках пути 'pizzas__toppings' был неявно создан queryset.

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

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

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

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

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

Подробнее о работе защиты Django от SQL-инъекций.

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

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

  • 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 не нужны.

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

    Например, это будет работать:

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

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

  • where / tables

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

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

    Пример:

    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() в начало построения queryset, чтобы ваша таблица использовалась первой. Наконец, если ничего не помогает, изучите сформированный запрос и перепишите добавление where, используя псевдоним, присвоенный дополнительной таблице. При одинаковом построении queryset псевдоним будет каждый раз одним и тем же, поэтому на его имя можно полагаться.

  • order_by

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

    Например:

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

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

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

  • params

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

    Пример:

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

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

    Неправильно:

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

    Правильно:

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

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

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

defer()

defer(*fields)

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

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

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

Queryset с отложенными полями по-прежнему возвращает экземпляры модели. Каждое отложенное поле будет загружено из базы данных при обращении к нему (по одному, а не все отложенные поля сразу).

Примечание

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

Можно вызывать 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()) с аргументом, полученным в результате агрегации (например, с использованием результата annotate()), не имеет смысла и вызовет исключение. Агрегированные значения всегда загружаются в результирующий queryset.

Примечание

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

Даже если вы считаете, что ваш сценарий относится к сложным, используйте defer() только тогда, когда во время загрузки queryset невозможно определить, понадобятся ли вам дополнительные поля. Если вы часто загружаете и используете определённую подгруппу данных, лучше нормализовать модели и поместить незагружаемые данные в отдельную модель (и таблицу базы данных). Если по какой-либо причине столбцы должны оставаться в одной таблице, создайте модель с 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.defer("f2")

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

Примечание

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

only()

only(*fields)

Метод only() по сути противоположен методу defer(). При вычислении queryset немедленно загружаются только поля, переданные этому методу и не указанные ранее как отложенные.

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

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

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 immediately.
Entry.objects.defer("body").only("headline", "body")

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

Если при использовании only() опустить поле, запрошенное с помощью select_related(), это также вызовет ошибку. С другой стороны, вызов only() без аргументов возвращает все поля (включая аннотации), загруженные queryset.

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

Примечание

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

Примечание

При вызове defer() после only() поля из defer() переопределят only() для полей, указанных в обоих методах.

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)

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

Например:

from django.db import transaction

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

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

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

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

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

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

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

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

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

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

Нельзя использовать 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 поддерживает только аргумент nowait, MariaDB 10.6+ также поддерживает аргумент skip_locked, а MySQL поддерживает аргументы nowait, skip_locked и of. Аргумент no_key поддерживается только в PostgreSQL.

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

Вычисление queryset с 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.

raw()

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

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

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

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

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

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

Объединяемые queryset должны относиться к одной и той же модели.

AND (&)

Объединяет два QuerySets с помощью SQL-оператора AND, аналогично последовательному применению фильтров.

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

Model.objects.filter(x=1) & Model.objects.filter(y=2)
Model.objects.filter(x=1).filter(y=2)

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

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

OR (|)

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

| — некоммутативная операция, поскольку могут быть сформированы разные (хотя и эквивалентные) запросы.

XOR (^)

Объединяет два QuerySets с помощью SQL-оператора XOR. Выражение XOR выбирает строки, соответствующие нечётному числу операндов.

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

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 XOR y=2

Примечание

XOR поддерживается в MariaDB и MySQL напрямую. В других СУБД x ^ y ^ ... ^ z преобразуется в эквивалентное выражение:

(x OR y OR ... OR z) AND
1=MOD(
    (CASE WHEN x THEN 1 ELSE 0 END) +
    (CASE WHEN y THEN 1 ELSE 0 END) +
    ...
    (CASE WHEN z THEN 1 ELSE 0 END),
    2
)

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

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

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

Поскольку эти методы вычисляют QuerySet, их вызов блокирует выполнение, поэтому основные (синхронные) версии нельзя вызывать из асинхронного кода. По этой причине у каждого метода есть соответствующая асинхронная версия с префиксом a — например, вместо get(…) можно вызвать await aget(…).

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

get()

get(*args, **kwargs)
aget(*args, **kwargs)

Асинхронная версия: aget()

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

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

Если вы ожидаете, что QuerySet уже вернёт одну строку, можно вызвать 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)
acreate(**kwargs)

Асинхронная версия: acreate()

Вспомогательный метод, который за один шаг создаёт объект и сохраняет его. Таким образом:

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)
aget_or_create(defaults=None, **kwargs)

Асинхронная версия: aget_or_create()

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

from django.db.models import Q

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

update_or_create()

update_or_create(defaults=None, create_defaults=None, **kwargs)
aupdate_or_create(defaults=None, create_defaults=None, **kwargs)

Асинхронная версия: aupdate_or_create()

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

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

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

Этот метод позволяет сократить шаблонный код. Например:

defaults = {"first_name": "Bob"}
create_defaults = {"first_name": "Bob", "birthday": date(1940, 10, 9)}
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(create_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"},
    create_defaults={"first_name": "Bob", "birthday": date(1940, 10, 9)},
)

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

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

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

bulk_create()

bulk_create(objs, batch_size=None, ignore_conflicts=False, update_conflicts=False, update_fields=None, unique_fields=None)
abulk_create(objs, batch_size=None, ignore_conflicts=False, update_conflicts=False, update_fields=None, unique_fields=None)

Асинхронная версия: abulk_create()

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

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

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

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

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

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

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

В поддерживающих эту возможность базах данных (во всех, кроме Oracle) установка параметра update_conflicts в True указывает базе данных обновить update_fields при конфликте во время добавления строки. В PostgreSQL и SQLite, помимо update_fields, необходимо передать список unique_fields, для которых возможен конфликт.

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

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

В MySQL и MariaDB установка параметра ignore_conflicts в True превращает некоторые типы ошибок, не связанные с дублированием ключей, в предупреждения. Это происходит даже в строгом режиме. Например, это относится к недопустимым значениям или нарушениям ограничений NOT NULL. Подробнее см. в документации MySQL и документации MariaDB.

bulk_update()

bulk_update(objs, fields, batch_size=None)
abulk_update(objs, fields, batch_size=None)

Асинхронная версия: abulk_update()

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

>>> objs = [
...     Entry.objects.create(headline="Entry 1"),
...     Entry.objects.create(headline="Entry 2"),
... ]
>>> objs[0].headline = "This is entry 1"
>>> objs[1].headline = "This is entry 2"
>>> Entry.objects.bulk_update(objs, ["headline"])
2

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

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

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

count()

count()
acount()

Асинхронная версия: acount()

Возвращает целое число, соответствующее количеству объектов в базе данных, удовлетворяющих 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(), не приведёт к дополнительному запросу к базе данных.

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

in_bulk()

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

Асинхронная версия: ain_bulk()

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

Например:

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

Если передать in_bulk() пустой список, будет возвращён пустой словарь.

iterator()

iterator(chunk_size=None)
aiterator(chunk_size=None)

Асинхронная версия: aiterator()

Вычисляет QuerySet (выполняя запрос) и возвращает итератор (см. PEP 234) для перебора результатов либо асинхронный итератор (см. PEP 492), если вызвать асинхронную версию aiterator.

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

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

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

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

Если QuerySet не выполняет предварительную загрузку связанных объектов, отсутствие значения для chunk_size приведёт к использованию Django значения по умолчанию 2000.

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

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

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

Если QuerySet не выполняет предварительную загрузку связанных объектов, отсутствие значения для chunk_size приведёт к использованию Django значения по умолчанию 2000. Это значение получено на основе расчёта, опубликованного в списке рассылки psycopg:

Если предположить, что строки содержат 10–20 столбцов с текстовыми и числовыми данными, при значении 2000 будет получено менее 100 КБ данных. Это кажется хорошим компромиссом между количеством переданных строк и объёмом данных, которые будут отброшены, если цикл завершится раньше времени.

latest()

latest(*fields)
alatest(*fields)

Асинхронная версия: alatest()

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

В этом примере возвращается самая поздняя запись 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)
aearliest(*fields)

Асинхронная версия: aearliest()

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

first()

first()
afirst()

Асинхронная версия: afirst()

Возвращает первый объект, соответствующий набору запросов, или 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()
alast()

Асинхронная версия: alast()

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

aggregate()

aggregate(*args, **kwargs)
aaggregate(*args, **kwargs)

Асинхронная версия: aaggregate()

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

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

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

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

>>> from django.db.models import Count
>>> Blog.objects.aggregate(Count("entry__authors"))
{'entry__authors__count': 16}

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

>>> Blog.objects.aggregate(number_of_authors=Count("entry__authors"))
{'number_of_authors': 16}

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

exists()

exists()
aexists()

Асинхронная версия: aexists()

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

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

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

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

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

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

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

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

contains()

contains(obj)
acontains(obj)

Асинхронная версия: acontains()

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

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

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

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

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

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

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

update()

update(**kwargs)
aupdate(**kwargs)

Асинхронная версия: aupdate()

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

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

MySQL не поддерживает обновления с предварительной выборкой из той же таблицы

В MySQL при фильтрации по связанным таблицам QuerySet.update() может выполнить SELECT, а затем UPDATE вместо одного UPDATE. Если между запросами происходят параллельные изменения, это может привести к состоянию гонки. Чтобы обеспечить атомарность, рассмотрите возможность использования транзакций или отказа от таких условий фильтрации в MySQL.

Наконец, помните, что 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()
Упорядоченный набор запросов

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

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

Примечание

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

delete()

delete()
adelete()

Асинхронная версия: adelete()

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

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

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

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

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

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

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

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

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

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

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

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

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

as_manager()

classmethod as_manager()

Метод класса, возвращающий экземпляр Manager с копией методов QuerySet. Подробнее см. в разделе Создание менеджера с методами QuerySet.

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

explain()

explain(format=None, **options)
aexplain(format=None, **options)

Асинхронная версия: aexplain()

Возвращает строку с планом выполнения 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 5.2:

Добавлена поддержка параметров memory и serialize в PostgreSQL 17+.

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 с учетом регистра; в SQLite contains работает как icontains. Подробнее см. примечание в документации по базе данных.

icontains

Проверка на вхождение без учета регистра.

Пример:

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

Эквивалентный запрос SQL:

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

Пользователям SQLite

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

in

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

Примеры:

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

Можно также использовать QuerySet для динамического вычисления списка значений вместо передачи списка литеральных значений:

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

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

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

gt

Больше чем.

Пример:

Entry.objects.filter(id__gt=4)

Эквивалентный запрос SQL:

SELECT ... WHERE id > 4;

gte

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

lt

Меньше чем.

lte

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

startswith

Начинается с, с учетом регистра.

Пример:

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

Эквивалентный запрос SQL:

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

SQLite не поддерживает операторы LIKE с учетом регистра; в SQLite startswith работает как istartswith.

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 с учетом регистра; в SQLite endswith работает как iendswith. Подробнее см. документацию в примечании о базе данных.

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

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

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

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

Для полей даты и даты со временем — точное совпадение по году. Позволяет объединять с дополнительными поисками по полям. Принимает целочисленное значение года.

Пример:

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

iso_year

Для полей даты и даты со временем — точное совпадение по году в соответствии с нумерацией недель ISO 8601. Позволяет объединять с дополнительными поисками по полям. Принимает целочисленное значение года.

Пример:

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

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

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

month

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

day

Для полей даты и даты со временем — точное совпадение по дню. Позволяет объединять с дополнительными поисками по полям. Принимает целочисленное значение дня.

Пример:

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 различается для разных движков баз данных.)

Обратите внимание: будут найдены все записи с датой публикации, приходящейся на третье число месяца, например 3 января, 3 июля и т. д.

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

week

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

week_day

Для полей даты и даты со временем — совпадение по дню недели. Позволяет объединять с дополнительными поисками по полям.

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

iso_week_day

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

quarter

Для полей даты и даты со временем — совпадение по кварталу года. Позволяет объединять с дополнительными поисками по полям. Принимает целое число от 1 до 4, обозначающее квартал года.

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

Entry.objects.filter(pub_date__quarter=2)

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

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

time

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

Пример:

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

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

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

hour

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

minute

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

second

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

isnull

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

Пример:

Entry.objects.filter(pub_date__isnull=True)

Эквивалентный запрос SQL:

SELECT ... WHERE pub_date IS NULL;

regex

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

Используется синтаксис регулярных выражений серверной части базы данных. В SQLite, где нет встроенной поддержки регулярных выражений, эта возможность реализована с помощью пользовательской функции REGEXP на Python, поэтому применяется синтаксис регулярных выражений модуля Python 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 не содержит записей или если в непустом QuerySet есть пустая группа. Чтобы возвращать другое значение, задайте аргумент default. Исключением из этого поведения является Count: она возвращает 0, если QuerySet пуст, поскольку Count не поддерживает аргумент default.

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

expressions

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

output_field

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

Примечание

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

filter

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

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

default

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

**extra

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

AnyValue

Добавлено в Django 6.0.
class AnyValue(expression, output_field=None, filter=None, default=None, **extra) [исходный код]

Возвращает произвольное значение из входных значений, отличных от NULL.

  • Псевдоним по умолчанию: <field>__anyvalue
  • Тип возвращаемого значения: совпадает с типом входного поля или output_field, если он задан. Если набор запросов или группа пусты, возвращается default.

Пример использования:

>>> # Get average rating for each year along with a sample headline
>>> # from that year.
>>> from django.db.models import AnyValue, Avg, F, Q
>>> sample_headline = AnyValue("headline")
>>> Entry.objects.values(
...     pub_year=F("pub_date__year"),
... ).annotate(
...     avg_rating=Avg("rating"),
...     sample_headline=sample_headline,
... )

>>> # Get a sample headline from each year with rating greater than 4.5.
>>> sample_headline = AnyValue(
...     "headline",
...     filter=Q(rating__gt=4.5),
... )
>>> Entry.objects.values(
...     pub_year=F("pub_date__year"),
... ).annotate(
...     avg_rating=Avg("rating"),
...     sample_headline=sample_headline,
... )

Поддерживается в SQLite, MySQL, Oracle и PostgreSQL 16+.

MySQL с включённым режимом ONLY_FULL_GROUP_BY

Если в MySQL включён режим SQL ONLY_FULL_GROUP_BY, может потребоваться использовать AnyValue, когда агрегирование включает сочетание агрегатных и неагрегатных функций. Использование AnyValue позволяет ссылаться на неагрегатную функцию в списке выборки, если база данных не может определить, что эта функция функционально зависит от столбцов в предложении GROUP BY. Подробнее см. в документации по агрегированию.

Avg

class Avg(expression, output_field=None, distinct=False, filter=None, default=None, **extra) [исходный код]

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

  • Псевдоним по умолчанию: <field>__avg
  • Тип возвращаемого значения: float, если входное значение — int; в противном случае совпадает с типом входного поля или output_field, если он задан. Если набор запросов или группа пусты, возвращается default.
distinct

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

Count

class Count(expression, distinct=False, filter=None, **extra) [исходный код]

Возвращает количество объектов, связанных через заданное выражение. Count('*') эквивалентно SQL-выражению COUNT(*).

  • Псевдоним по умолчанию: <field>__count
  • Тип возвращаемого значения: int
distinct

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

Примечание

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

Max

class Max(expression, output_field=None, filter=None, default=None, **extra) [исходный код]

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

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

Min

class Min(expression, output_field=None, filter=None, default=None, **extra) [исходный код]

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

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

StdDev

class StdDev(expression, output_field=None, sample=False, filter=None, default=None, **extra) [исходный код]

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

  • Псевдоним по умолчанию: <field>__stddev
  • Тип возвращаемого значения: float, если входное значение — int; в противном случае совпадает с типом входного поля или output_field, если он задан. Если набор запросов или группа пусты, возвращается default.
sample

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

Sum

class Sum(expression, output_field=None, distinct=False, filter=None, default=None, **extra) [исходный код]

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

  • Псевдоним по умолчанию: <field>__sum
  • Тип возвращаемого значения: совпадает с типом входного поля или output_field, если он задан. Если набор запросов или группа пусты, возвращается default.
distinct

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

Variance

class Variance(expression, output_field=None, sample=False, filter=None, default=None, **extra) [исходный код]

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

  • Псевдоним по умолчанию: <field>__variance
  • Тип возвращаемого значения: float, если входное значение — int; в противном случае совпадает с типом входного поля или output_field, если он задан. Если набор запросов или группа пусты, возвращается default.
sample

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

StringAgg

Добавлено в Django 6.0.
class StringAgg(expression, delimiter, output_field=None, distinct=False, filter=None, order_by=None, default=None, **extra) [исходный код]

Возвращает входные значения, объединённые в строку и разделённые строкой delimiter, или default, если значений нет.

  • Псевдоним по умолчанию: <field>__stringagg
  • Тип возвращаемого значения: string или output_field, если он задан. Если набор запросов или группа пусты, возвращается default.
delimiter

Объект Value или выражение, представляющее строку-разделитель между значениями. Например, Value(",").

Инструменты для работы с запросами

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

Объекты Q()

class Q [исходный код]

Объект Q() представляет собой условие SQL, которое можно использовать в операциях, связанных с базой данных. Он похож на объект F(), представляющий значение поля модели или аннотации. С их помощью можно определять условия и повторно использовать их. Условия можно инвертировать оператором ~ (NOT) и объединять с помощью таких операторов, как | (OR), & (AND) и ^ (XOR). См. раздел Сложные поисковые запросы с объектами 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"))
<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) [исходный код]
aprefetch_related_objects(model_instances, *related_lookups)

Асинхронная версия: aprefetch_related_objects()

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

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

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

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

Объекты FilteredRelation()

class FilteredRelation(relation_name, *, condition=Q()) [исходный код]
relation_name

Имя поля, по которому нужно фильтровать связь.

condition

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

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

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

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

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

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

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

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

  • QuerySet.only() и prefetch_related().
  • GenericForeignKey, унаследованный от родительской модели.

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

Spec-Zone.ru

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