Spec-Zone.ru › Django 5.2

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

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

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

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

Внутренне, 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)
    

    Синхронные и асинхронные итераторы QuerySets используют один и тот же кэш.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

QuerySet API

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

class QuerySet(model=None, query=None, using=None, hints=None) [source]

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

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

ordered [source]

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

db [source]

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

Примечание

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

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

Примечание

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

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

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

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

Примечание

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

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

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

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

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

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

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

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

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

Примечание

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

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

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

values()

values(*fields, **expressions)

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

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

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

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

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

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

Пример:

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

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

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

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

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

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

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

Несколько нюансов, на которые стоит обратить внимание:

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

    Например:

    >>> Entry.objects.values()
    <QuerySet [{'blog_id': 1, 'headline': 'First Entry', ...}, ...]>
    
    >>> Entry.objects.values("blog")
    <QuerySet [{'blog': 1}, ...]>
    
    >>> Entry.objects.values("blog_id")
    <QuerySet [{'blog_id': 1}, ...]>
    
  • При использовании values() вместе с distinct(), имейте в виду, что сортировка может повлиять на результаты. См. примечание в distinct() для подробностей.
  • Если вы используете условие values() после вызова extra(), любые поля, определённые аргументом select в вызове extra(), должны быть явно включены в вызов values(). Любой вызов extra(), сделанный после вызова values(), проигнорирует дополнительные выбранные поля.
  • Вызов only() и defer() после values() не имеет смысла, поэтому это приведёт к ошибке TypeError.
  • Сочетание преобразований и агрегаций требует использования двух вызовов annotate(), явно или в качестве именованных аргументов вызова values(). Как и выше, если преобразование зарегистрировано для соответствующего типа поля, первый вызов annotate() можно опустить, поэтому следующие примеры эквивалентны:

    >>> from django.db.models import CharField, Count
    >>> from django.db.models.functions import Lower
    >>> CharField.register_lookup(Lower)
    >>> Blog.objects.values("entry__authors__name__lower").annotate(entries=Count("entry"))
    <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]>
    >>> Blog.objects.values(entry__authors__name__lower=Lower("entry__authors__name")).annotate(
    ...     entries=Count("entry")
    ... )
    <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]>
    >>> Blog.objects.annotate(entry__authors__name__lower=Lower("entry__authors__name")).values(
    ...     "entry__authors__name__lower"
    ... ).annotate(entries=Count("entry"))
    <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]>
    

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

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

Blog.objects.values().order_by("id")
Blog.objects.order_by("id").values()

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

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

>>> Blog.objects.values("name", "entry__headline")
<QuerySet [{'name': 'My blog', 'entry__headline': 'An entry'},
     {'name': 'My blog', 'entry__headline': 'Another entry'}, ...]>

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

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

Специальные значения для JSONField в SQLite

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

Изменено в 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

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

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

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

dates()

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

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

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

Примеры:

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

all()

all()

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

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

union()

union(*other_qs, all=False)

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

>>> qs1.union(qs2, qs3)

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

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

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

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

intersection()

intersection(*other_qs)

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

>>> qs1.intersection(qs2, qs3)

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

difference()

difference(*other_qs)

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

>>> qs1.difference(qs2, qs3)

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

select_related()

select_related(*fields)

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

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

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

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

А вот поиск с использованием select_related:

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

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

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

from django.utils import timezone

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

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

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

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

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

from django.db import models


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


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


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

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

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

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

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

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

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

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

>>> without_relations = queryset.select_related(None)

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

prefetch_related()

prefetch_related(*lookups)

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

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

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

prefetch_related, с другой стороны, выполняет отдельный поиск для каждого отношения и выполняет «объединение» в Python. Это позволяет ему предварительно загружать данные для отношений «многие ко многим», «многие ко одному» и GenericRelation объектов, что невозможно сделать с помощью select_related, в дополнение к внешним ключам и отношениям «один к одному», которые поддерживаются select_related. Он также поддерживает предварительную загрузку GenericForeignKey, однако, набор данных для каждого 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.

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

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

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

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

Примечание

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

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

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

Кроме того, если вы вызовете методы, изменяющие базу данных add(), 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")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Примечание

Порядок запросов важен.

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

>>> prefetch_related("pizzas__toppings", "pizzas")

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

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

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

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

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

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

extra()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • 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"),
    )
    

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

  • where / tables

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

    where и tables оба принимают список строк. Все параметры where "И" с любыми другими критериями поиска.

    Пример:

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

    …примерно переводится в следующий SQL:

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

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

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

  • order_by

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

    Например:

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

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

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

  • params

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

    Пример:

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

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

    Плохо:

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

    Хорошо:

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

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

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

defer()

defer(*fields)

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

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

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

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

Примечание

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

Примечание

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

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

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

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

    class Meta:
        managed = False
        db_table = "app_largetable"


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

    class Meta:
        db_table = "app_largetable"


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

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

Примечание

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

only()

only(*fields)

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

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

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

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

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

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

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

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

# Final result loads headline immediately.
Entry.objects.defer("body").only("headline", "body")

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

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

Как и с 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)

Возвращает набор запросов, который заблокирует строки до конца транзакции, генерируя оператор 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:
        ...

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

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

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

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

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

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

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

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

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

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

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

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

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

postgresql, oracle, и mysql бэкэнды баз данных поддерживают select_for_update(). Однако, MariaDB поддерживает только аргумент 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. Это предотвращает неожиданное блокирование кода.

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

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

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

И (&)

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

ИЛИ (|)

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

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

Исключающее ИЛИ (^)

Объединяет два набора запросов, используя оператор 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))

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

from django.db.models import Q

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

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

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

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

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

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

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

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

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

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

Предположим следующие модели:

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


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

Вы можете использовать get_or_create() через поле глав книги, но это только запрос в контексте этой книги:

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

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

update_or_create()

update_or_create(defaults=None, 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()

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

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

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

  • Метод save() модели не будет вызван, и сигналы pre_save и post_save не будут отправлены.
  • Он не работает с дочерними моделями в сценарии наследования с несколькими таблицами.
  • Если первичный ключ модели — AutoField и 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 преобразует некоторые типы ошибок, помимо ошибки дублирования ключа, в предупреждения. Даже в режиме строгости. Например: недействительные значения или нарушения обязательности. Подробнее см. документацию 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().

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

Пример:

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

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

iterator()

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

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

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

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

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

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

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

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

Пока набор запросов не предварительно выбирает связанные объекты, отсутствие значения для 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"))
{'entry__count': 16}

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

>>> Blog.objects.aggregate(number_of_entries=Count("entry"))
{'number_of_entries': 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.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().

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

ForeignKeys, которые установлены в 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.1:

Добавлена поддержка опции generic_plan для PostgreSQL 16+.

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

icontains

Регистронезависимое проверка вхождения.

Пример:

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

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

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

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

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

in

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

Примеры:

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

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

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

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

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

Этот набор запросов будет оцениваться как подзапрос:

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

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

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

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

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

Соображения по производительности

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

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

Обратите внимание на list() вызов вокруг блога 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; startswith работает как istartswith в SQLite.

istartswith

Регистронезависимое начинается с.

Пример:

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

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

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

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

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

endswith

Регистрозависимое заканчивается на.

Пример:

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

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

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

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

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

iendswith

Регистронезависимое заканчивается на.

Пример:

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

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

SELECT ... WHERE headline ILIKE '%Lennon'

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

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

range

Проверка диапазона (включая границы).

Пример:

import datetime

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

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

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

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

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

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

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

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

date

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

Пример:

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

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

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

year

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

Пример:

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

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

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

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

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

iso_year

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

Пример:

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

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

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

month

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

Пример:

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

SQL-эквивалент:

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

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

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

day

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

Пример:

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

SQL-эквивалент:

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

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

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

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

week

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

Пример:

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

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

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

week_day

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

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

Пример:

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

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

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

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

iso_week_day

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

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

Пример:

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

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

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

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

quarter

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

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

Entry.objects.filter(pub_date__quarter=2)

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

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

time

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

Пример:

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

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

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

hour

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

Пример:

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

SQL-эквивалент:

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

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

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

minute

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

Пример:

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

SQL-эквивалент:

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

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

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

second

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

Пример:

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

SQL-эквивалент:

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

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

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

isnull

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

Пример:

Entry.objects.filter(pub_date__isnull=True)

SQL-эквивалент:

SELECT ... WHERE pub_date IS NULL;

regex

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

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

Пример:

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

SQL-эквиваленты:

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

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

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

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

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

iregex

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

Пример:

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

SQL-эквиваленты:

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

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

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

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

Функции агрегирования

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

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

SQLite не может обрабатывать агрегирование по полям даты/времени «из коробки». Это связано с тем, что в 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-запроса, сгенерированного агрегатом.

Avg

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

Возвращает среднее значение заданного выражения, которое должно быть числовым, если вы не укажете другое 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) [source]

Возвращает количество объектов, связанных через предоставленное выражение. 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) [source]

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

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

Min

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

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

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

StdDev

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

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

  • Значение по умолчанию: <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) [source]

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

  • Значение по умолчанию: <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) [source]

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

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

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

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

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

Q() объекты

class Q [source]

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

Prefetch() объекты

class Prefetch(lookup, queryset=None, to_attr=None) [source]

Объект 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) [source]
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()) [source]
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/5.2/ref/models/querysets/

Spec-Zone.ru

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