Справочник API QuerySet
В данном документе описываются детали API QuerySet. Он основан на материалах, представленных в руководствах по моделям и запросам к базе данных, поэтому, вероятно, вам следует ознакомиться с этими документами перед чтением данного.
В этом справочнике мы будем использовать пример моделей блога, представленные в руководстве по запросам к базе данных.
Когда QuerySet оцениваются
Внутренне, QuerySet может быть создан, отфильтрован, разбит на части и вообще передаваться, не обращаясь к базе данных. Действительно, ни одно действие с базой данных не происходит, пока вы не сделаете что-то, чтобы оценить запрос.
Вы можете оценить QuerySet следующими способами:
-
Итерация.
QuerySetитерируемый, и он выполняет свой запрос к базе данных в первый раз, когда вы итерируетесь по нему. Например, это выведет заголовок всех записей в базе данных:for e in Entry.objects.all(): print(e.headline)Примечание: Не используйте это, если вам нужно только определить, существует ли хотя бы один результат. Эффективнее использовать
exists(). -
Асинхронная итерация.
QuerySetтакже может быть итерирован с помощьюasync for:async for e in Entry.objects.all(): results.append(e)Синхронные и асинхронные итераторы QuerySets используют один и тот же кэш.
Изменено в Django 4.1:Добавлена поддержка асинхронной итерации.
-
Разбиение. Как объяснено в Ограничение QuerySets,
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, приведет к выполнению запроса. Если существует хотя бы один результат,QuerySetTrue, в противном случае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'}]>
QuerySet API
Вот формальное объявление QuerySet:
-
class QuerySet(model=None, query=None, using=None, hints=None) -
Обычно при взаимодействии с
QuerySetвы будете использовать его, цепляя фильтры. Для этого большинство методовQuerySetвозвращают новые querysets. Эти методы подробно описаны позже в этом разделе.Класс
QuerySetимеет следующие публичные атрибуты, которые можно использовать для интроспекции:-
ordered -
TrueеслиQuerySetотсортирован — т.е. имеет условиеorder_by()или стандартную сортировку модели.Falseв противном случае.
-
db -
База данных, которая будет использоваться, если этот запрос будет выполнен сейчас.
Примечание
Параметр
queryдляQuerySetсуществует для того, чтобы специализированные подклассы запросов могли восстановить внутреннее состояние запроса. Значение параметра — непрозрачное представление этого состояния запроса и не входит в состав публичного API. -
Методы, возвращающие новые QuerySet
Django предоставляет ряд методов уточнения QuerySet , которые изменяют либо типы результатов, возвращаемых QuerySet, либо способ выполнения его SQL-запроса.
Примечание
Эти методы не выполняют запросов к базе данных, поэтому они безопасны для использования в асинхронном коде и не имеют отдельных асинхронных версий.
filter()
-
filter(*args, **kwargs)
Возвращает новый QuerySet, содержащий объекты, которые соответствуют заданным параметрам поиска.
Параметры поиска (**kwargs) должны быть в формате, описанном в Поисках по полям ниже. Несколько параметров объединяются через AND в подлежащем SQL-запросе.
Если вам нужны более сложные запросы (например, запросы с операторами OR), вы можете использовать Q objects (*args).
exclude()
-
exclude(*args, **kwargs)
Возвращает новый QuerySet, содержащий объекты, которые не соответствуют заданным параметрам поиска.
Параметры поиска (**kwargs) должны быть в формате, описанном в Поисках по полям ниже. Несколько параметров объединяются через AND в подлежащем SQL-запросе, и всё это заключается в NOT().
В этом примере исключаются все записи, дата pub_date которых позже 2005-1-3 И заголовок headline которых «Hello»:
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline="Hello")
В SQL это эквивалентно:
SELECT ... WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')
В этом примере исключаются все записи, дата pub_date которых позже 2005-1-3 ИЛИ заголовок которых «Hello»:
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline="Hello")
В SQL это эквивалентно:
SELECT ... WHERE NOT pub_date > '2005-1-3' AND NOT headline = 'Hello'
Обратите внимание на то, что второй пример более строгий.
Если вам нужно выполнить более сложные запросы (например, запросы с OR операторами), вы можете использовать Q objects (*args).
annotate()
-
annotate(*args, **kwargs)
Добавляет к каждому объекту в QuerySet список предоставленных выражений запроса. Выражение может быть простым значением, ссылкой на поле в модели (или любой связанной модели) или агрегативным выражением (среднее, сумма и т.д.), вычисленным по объектам, связанным с объектами в 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'], то первый queryset был бы идентичен:
Entry.objects.order_by("blog__name")
Вы также можете отсортировать по выражениям запроса вызвав asc() или desc() для выражения:
Entry.objects.order_by(Coalesce("summary", "headline").desc())
asc() и desc() имеют аргументы (nulls_first и nulls_last), которые управляют тем, как сортируются нулевые значения.
Будьте осторожны при сортировке по полям связанных моделей, если вы также используете distinct(). См. примечание в distinct() для объяснения того, как сортировка по связанным моделям может изменить ожидаемые результаты.
Примечание
Разрешается указать многозначное поле для сортировки результатов (например, поле ManyToManyField или обратное отношение поля ForeignKey).
Рассмотрим этот случай:
class Event(Model):
parent = models.ForeignKey(
"self",
on_delete=models.CASCADE,
related_name="children",
)
date = models.DateField()
Event.objects.order_by("children__date")
Здесь потенциально может быть несколько данных сортировки для каждого Event; каждый Event с несколькими children будет возвращён несколько раз в новый QuerySet, который order_by() создаёт. Другими словами, использование order_by() на QuerySet может вернуть больше элементов, чем вы обрабатывали в начале — что, вероятно, не ожидалось и не полезно.
Следовательно, будьте осторожны при использовании многозначных полей для сортировки результатов. Если вы уверены, что для каждого элемента, по которому вы сортируете, будет только один элемент сортировки, этот подход не должен вызывать проблем. Если нет, убедитесь, что результаты соответствуют вашим ожиданиям.
Нет способа указать, должна ли сортировка быть регистрозависимой. Что касается регистрозависимости, Django отсортирует результаты так, как обычно это делает ваша база данных.
Вы можете отсортировать по полю, преобразованному в нижний регистр, с помощью Lower, чтобы достичь регистрозависимой сортировки:
Entry.objects.order_by(Lower("headline").desc())
Если вы не хотите применять никакой сортировки к запросу, даже стандартной, вызовите order_by() без параметров.
Вы можете узнать, отсортирован ли запрос или нет, проверив атрибут QuerySet.ordered, который будет True если запрос был отсортирован каким-либо образом.
Каждый вызов order_by() очистит все предыдущие сортировки. Например, этот запрос будет отсортирован по pub_date, а не по headline:
Entry.objects.order_by("headline").order_by("pub_date")
Предупреждение
Сортировка — это не бесплатная операция. Каждое поле, которое вы добавляете в порядок сортировки, влечёт затраты для вашей базы данных. Каждый внешний ключ будет подразумевать также все свои стандартные порядки сортировки.
Если в запросе не указан порядок сортировки, результаты возвращаются из базы данных в неуказанном порядке. Гарантируется только определённый порядок, когда сортировка выполняется по набору полей, уникально идентифицирующих каждый объект в результатах. Например, если поле name не уникально, сортировка по нему не гарантирует, что объекты с одинаковым именем всегда будут отображаться в одном и том же порядке.
reverse()
-
reverse()
Используйте метод reverse() для изменения порядка возвращения элементов в queryset. Вызов reverse() второй раз восстановит порядок в исходном направлении.
Чтобы получить «последние» пять элементов в queryset, вы можете сделать это так:
my_queryset.reverse()[:5]
Обратите внимание, что это не совсем то же самое, что срезы с конца последовательности в Python. Приведенный выше пример вернет последний элемент первым, затем предпоследний и так далее. Если бы у нас была последовательность Python и мы бы посмотрели на seq[-5:], мы бы увидели пятый элемент с конца первым. Django не поддерживает такой режим доступа (срез с конца), потому что сделать это эффективно в SQL невозможно.
Также обратите внимание, что reverse() следует вызывать только на QuerySet, у которого есть определенный порядок (например, при запросе к модели, которая определяет порядок по умолчанию, или при использовании order_by()). Если такой порядок не определен для данного QuerySet, вызов reverse() на нем не оказывает никакого реального эффекта (порядок был неопределенным до вызова reverse(), и останется неопределенным после).
distinct()
-
distinct(*fields)
Возвращает новый QuerySet, который использует SELECT DISTINCT в своем SQL-запросе. Это устраняет дублирующие строки из результатов запроса.
По умолчанию QuerySet не будет устранять дублирующие строки. На практике это редко проблема, потому что простые запросы, такие как Blog.objects.all(), не вводят возможность дублирования строк результатов. Однако, если ваш запрос охватывает несколько таблиц, возможно получение дублирующих результатов при вычислении QuerySet. Именно тогда вы бы использовали distinct().
Примечание
Любые поля, используемые в вызове order_by(), включаются в SQL SELECT столбцы. Это иногда приводит к неожиданным результатам при использовании совместно с distinct(). Если вы сортируете по полям из связанной модели, эти поля будут добавлены в выбранные столбцы, и они могут заставить иначе дублирующие строки казаться отличными. Поскольку дополнительные столбцы не отображаются в возвращаемых результатах (они присутствуют только для поддержки сортировки), иногда кажется, что возвращаются недубликатные результаты.
Аналогично, если вы используете запрос values() для ограничения выбираемых столбцов, столбцы, используемые в любом вызове order_by() (или порядке модели по умолчанию), по-прежнему будут участвовать и могут повлиять на уникальность результатов.
Вывод в данном случае состоит в том, что при использовании distinct() будьте осторожны при сортировке по связанным моделям. Аналогично, при использовании distinct() и values() вместе, будьте осторожны при сортировке по полям, не содержащимся в вызове values().
Только для PostgreSQL вы можете передавать позиционные аргументы (*fields) для указания имен полей, к которым должен применяться DISTINCT. Это приводит к SELECT DISTINCT ON SQL-запросу. Вот в чем разница. Для обычного вызова distinct() база данных сравнивает каждое поле в каждой строке при определении, какие строки являются уникальными. Для вызова distinct() с указанными именами полей база данных будет сравнивать только указанные имена полей.
Примечание
Когда вы указываете имена полей, вы обязательно должны предоставить order_by() в QuerySet, и поля в order_by() должны начинаться с полей в distinct(), в том же порядке.
Например, SELECT DISTINCT ON (a) дает вам первую строку для каждого значения в столбце a. Если вы не указываете порядок, вы получите какую-то произвольную строку.
Примеры (те, что после первого, будут работать только на PostgreSQL):
>>> Author.objects.distinct()
[...]
>>> Entry.objects.order_by("pub_date").distinct("pub_date")
[...]
>>> Entry.objects.order_by("blog").distinct("blog")
[...]
>>> Entry.objects.order_by("author", "pub_date").distinct("author", "pub_date")
[...]
>>> Entry.objects.order_by("blog__name", "mod_date").distinct("blog__name", "mod_date")
[...]
>>> Entry.objects.order_by("author", "pub_date").distinct("author")
[...]
Примечание
Помните, что order_by() использует любой порядок связанной модели по умолчанию, который был определен. Вам может потребоваться явно указать сортировку по отношению _id или сортировку по ссылкам, чтобы убедиться, что DISTINCT ON выражения совпадают с теми, что находятся в начале ORDER BY фразы. Например, если модель Blog определила ordering по name:
Entry.objects.order_by("blog").distinct("blog")
…не сработает, потому что запрос будет отсортирован по blog__name, таким образом нарушая совпадение DISTINCT ON выражения. Вам нужно будет явно указать сортировку по отношению _id полю (blog_id в данном случае) или сортировку по связанному (blog__pk) полю, чтобы убедиться, что оба выражения совпадают.
values()
-
values(*fields, **expressions)
Возвращает QuerySet, который возвращает словари, а не экземпляры моделей, когда используется в качестве итерируемого объекта.
Каждый из этих словарей представляет объект, при этом ключи соответствуют именам атрибутов объектов модели.
Этот пример сравнивает словари values() с обычными объектами модели:
# This list contains a Blog object.
>>> Blog.objects.filter(name__startswith="Beatles")
<QuerySet [<Blog: Beatles Blog>]>
# This list contains a dictionary.
>>> Blog.objects.filter(name__startswith="Beatles").values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
Метод values() принимает необязательные позиционные аргументы, *fields, которые указывают имена полей, к которым SELECT должно быть ограничено. Если вы указываете поля, каждый словарь будет содержать только ключи/значения поля для указанных вами полей. Если вы не указываете поля, каждый словарь будет содержать ключ и значение для каждого поля в таблице базы данных.
Пример:
>>> Blog.objects.values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
>>> Blog.objects.values("id", "name")
<QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>
Метод values() также принимает необязательные именованные аргументы, **expressions, которые передаются в annotate():
>>> from django.db.models.functions import Lower
>>> Blog.objects.values(lower_name=Lower("name"))
<QuerySet [{'lower_name': 'beatles blog'}]>
Вы можете использовать встроенные и пользовательские функции в сортировке. Например:
>>> from django.db.models import CharField
>>> from django.db.models.functions import Lower
>>> CharField.register_lookup(Lower)
>>> Blog.objects.values("name__lower")
<QuerySet [{'name__lower': 'beatles blog'}]>
Агрегат в values() фразе применяется до других аргументов в той же values() фразе. Если вам нужно сгруппировать по другому значению, добавьте его в предыдущую values() фразу.
>>> from django.db.models import Count
>>> Blog.objects.values("entry__authors", entries=Count("entry"))
<QuerySet [{'entry__authors': 1, 'entries': 20}, {'entry__authors': 1, 'entries': 13}]>
>>> Blog.objects.values("entry__authors").annotate(entries=Count("entry"))
<QuerySet [{'entry__authors': 1, 'entries': 33}]>
Некоторые тонкости, которые стоит упомянуть:
-
Если у вас есть поле, называемое
foo, которое являетсяForeignKey, вызовvalues()по умолчанию вернет словарь с ключомfoo_id, так как это имя скрытого атрибута модели, хранящего фактическое значение (атрибутfooссылается на связанную модель). Когда вы вызываетеvalues()и передаете имена полей, вы можете передать либоfoo, либоfoo_id, и вы получите то же самое (ключ словаря будет совпадать с именем поля, которое вы передали).Например:
>>> Entry.objects.values() <QuerySet [{'blog_id': 1, 'headline': 'First Entry', ...}, ...]> >>> Entry.objects.values("blog") <QuerySet [{'blog': 1}, ...]> >>> Entry.objects.values("blog_id") <QuerySet [{'blog_id': 1}, ...]> - При использовании
values()вместе сdistinct(), имейте в виду, что сортировка может повлиять на результаты. См. примечание вdistinct()для получения дополнительной информации. - Если вы используете
values()фразу после вызоваextra(), любые поля, определенные аргументомselectвextra(), должны быть явно включены в вызовvalues(). Любой вызовextra(), выполненный после вызоваvalues(), будет игнорировать дополнительные выбранные поля. - Вызов
only()иdefer()послеvalues()не имеет смысла, поэтому это приведет к ошибкеTypeError. -
Комбинирование преобразований и агрегатов требует использования двух вызовов
annotate(), явно или в качестве именованных аргументов дляvalues(). Как и выше, если преобразование зарегистрировано для соответствующего типа поля, первый вызовannotate()можно опустить, поэтому следующие примеры эквивалентны:>>> from django.db.models import CharField, Count >>> from django.db.models.functions import Lower >>> CharField.register_lookup(Lower) >>> Blog.objects.values("entry__authors__name__lower").annotate(entries=Count("entry")) <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]> >>> Blog.objects.values(entry__authors__name__lower=Lower("entry__authors__name")).annotate( ... entries=Count("entry") ... ) <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]> >>> Blog.objects.annotate(entry__authors__name__lower=Lower("entry__authors__name")).values( ... "entry__authors__name__lower" ... ).annotate(entries=Count("entry")) <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]>
Это полезно, когда вы знаете, что вам понадобятся только значения из небольшого числа доступных полей, и вам не понадобится функциональность объекта модели. Эффективнее выбрать только необходимые поля.
Наконец, обратите внимание, что вы можете вызвать filter(), order_by(), и т. д. после вызова values(), что означает, что эти два вызова идентичны:
Blog.objects.values().order_by("id")
Blog.objects.order_by("id").values()
Разработчики Django предпочитают сначала располагать все методы, влияющие на SQL, а затем (необязательно) любые методы, влияющие на вывод (например, values()), но это не имеет значения. Это ваш шанс проявить индивидуальность.
Вы также можете ссылаться на поля в связанных моделях с обратными отношениями через атрибуты OneToOneField, ForeignKey и ManyToManyField.
>>> Blog.objects.values("name", "entry__headline")
<QuerySet [{'name': 'My blog', 'entry__headline': 'An entry'},
{'name': 'My blog', 'entry__headline': 'Another entry'}, ...]>
Предупреждение
Поскольку атрибуты ManyToManyField и обратные связи могут иметь несколько связанных строк, включение их может иметь множительный эффект на размер вашего набора результатов. Это будет особенно заметно, если вы включите несколько таких полей в свой values() запрос, в этом случае будут возвращены все возможные комбинации.
Специальные значения для JSONField в SQLite
Из-за того, как реализованы SQL-функции JSON_EXTRACT и JSON_TYPE в SQLite, и отсутствия типа данных BOOLEAN, values() вернет True, False, и None вместо строк "true", "false", и "null" для преобразований ключей JSONField.
values_list()
-
values_list(*fields, flat=False, named=False)
Это похоже на values() за исключением того, что вместо возвращения словарей, при итерировании возвращаются кортежи. Каждый кортеж содержит значение из соответствующего поля или выражения, переданного в вызов values_list() — таким образом, первый элемент — это первое поле и т. д. Например:
>>> Entry.objects.values_list("id", "headline")
<QuerySet [(1, 'First entry'), ...]>
>>> from django.db.models.functions import Lower
>>> Entry.objects.values_list("id", Lower("headline"))
<QuerySet [(1, 'first entry'), ...]>
Если вы передаете только одно поле, вы также можете передать параметр flat. Если True, это означает, что возвращаемые результаты будут одиночными значениями, а не кортежами с одним элементом. Пример продемонстрирует разницу:
>>> Entry.objects.values_list("id").order_by("id")
<QuerySet[(1,), (2,), (3,), ...]>
>>> Entry.objects.values_list("id", flat=True).order_by("id")
<QuerySet [1, 2, 3, ...]>
Ошибка возникает при передаче flat при наличии более одного поля.
Вы можете передать named=True для получения результатов как namedtuple():
>>> Entry.objects.values_list("id", "headline", named=True)
<QuerySet [Row(id=1, headline='First entry'), ...]>
Использование именованного кортежа может сделать использование результатов более читабельным, за счет небольшого штрафа производительности при преобразовании результатов в именованный кортеж.
Если вы не передаете никаких значений в values_list(), оно вернет все поля модели в том порядке, в котором они были объявлены.
Частая необходимость заключается в получении значения конкретного поля определенного экземпляра модели. Для этого используйте values_list() и вызов get():
>>> Entry.objects.values_list("headline", flat=True).get(pk=1)
'First entry'
values() и values_list() предназначены как оптимизации для определенного случая использования: извлечение подмножества данных без накладных расходов на создание экземпляра модели. Эта метафора перестает работать при работе со многими-ко-многим и другими многозначными отношениями (такими как один-ко-многим отношение для обратного внешнего ключа), поскольку предположение «одна строка, один объект» не выполняется.
Например, обратите внимание на поведение при запросе через ManyToManyField:
>>> Author.objects.values_list("name", "entry__headline")
<QuerySet [('Noam Chomsky', 'Impressions of Gaza'),
('George Orwell', 'Why Socialists Do Not Believe in Fun'),
('George Orwell', 'In Defence of English Cooking'),
('Don Quixote', None)]>
Авторы с несколькими записями появляются несколько раз, а авторы без записей имеют None для заголовка записи.
Аналогично, при запросе обратного внешнего ключа None появляется для записей, у которых нет авторов:
>>> Entry.objects.values_list("authors")
<QuerySet [('Noam Chomsky',), ('George Orwell',), (None,)]>
Специальные значения для JSONField в SQLite
Из-за того, как реализованы SQL-функции JSON_EXTRACT и JSON_TYPE в SQLite, и отсутствия типа данных BOOLEAN, values_list() вернет True, False, и None вместо строк "true", "false", и "null" для преобразований ключей JSONField.
dates()
-
dates(field, kind, order='ASC')
Возвращает QuerySet, который вычисляется в список объектов datetime.date, представляющих все доступные даты определенного вида в содержании QuerySet.
field должно быть именем DateField вашей модели. kind должно быть "year", "month", "week", или "day". Каждый объект datetime.date в списке результатов «обрезается» до указанного type.
-
"year"возвращает список всех уникальных значений года для поля. -
"month"возвращает список всех уникальных значений год/месяц для поля. -
"week"возвращает список всех уникальных значений год/неделя для поля. Все даты будут понедельниками. -
"day"возвращает список всех уникальных значений год/месяц/день для поля.
order, которое по умолчанию равно 'ASC', должно быть либо 'ASC', либо 'DESC'. Это определяет, как упорядочить результаты.
Примеры:
>>> Entry.objects.dates("pub_date", "year")
[datetime.date(2005, 1, 1)]
>>> Entry.objects.dates("pub_date", "month")
[datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)]
>>> Entry.objects.dates("pub_date", "week")
[datetime.date(2005, 2, 14), datetime.date(2005, 3, 14)]
>>> Entry.objects.dates("pub_date", "day")
[datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)]
>>> Entry.objects.dates("pub_date", "day", order="DESC")
[datetime.date(2005, 3, 20), datetime.date(2005, 2, 20)]
>>> Entry.objects.filter(headline__contains="Lennon").dates("pub_date", "day")
[datetime.date(2005, 3, 20)]
datetimes()
-
datetimes(field_name, kind, order='ASC', tzinfo=None, is_dst=None)
Возвращает QuerySet, который вычисляется в список объектов datetime.datetime, представляющих все доступные даты определенного вида в содержании QuerySet.
field_name должно быть именем DateTimeField вашей модели.
kind должно быть либо "year", "month", "week", "day", "hour", "minute", или "second". Каждый объект datetime.datetime в списке результатов «обрезается» до указанного type.
order, которое по умолчанию равно 'ASC', должно быть либо 'ASC', либо 'DESC'. Это определяет, как упорядочить результаты.
tzinfo определяет часовой пояс, в который преобразуются даты и время до усечения. Действительно, заданное значение datetime имеет разные представления в зависимости от используемого часового пояса. Этот параметр должен быть объектом datetime.tzinfo. Если это None, Django использует текущий часовой пояс. Он не оказывает никакого влияния, когда USE_TZ равно False.
is_dst указывает, интерпретирует ли pytz несуществующие и неоднозначные значения datetime в летнее время. По умолчанию (если is_dst=None), pytz генерирует исключение для таких значений datetime.
Устарело начиная с версии 4.0: Параметр is_dst устарел и будет удален в Django 5.0.
Примечание
Эта функция выполняет преобразования часовых поясов непосредственно в базе данных. Вследствие этого ваша база данных должна уметь интерпретировать значение tzinfo.tzname(None).
- SQLite: никаких требований. Преобразования выполняются в Python.
- PostgreSQL: никаких требований (см. Часовые пояса).
- Oracle: никаких требований (см. Выбор файла часового пояса).
- MySQL: загрузить таблицы часовых поясов с помощью mysql_tzinfo_to_sql.
none()
-
none()
Вызов none() создаст набор запросов, который никогда не возвращает объекты, и ни один запрос не будет выполнен при доступе к результатам. Набор запросов qs.none() — это экземпляр EmptyQuerySet.
Примеры:
>>> Entry.objects.none() <QuerySet []> >>> from django.db.models.query import EmptyQuerySet >>> isinstance(Entry.objects.none(), EmptyQuerySet) True
all()
-
all()
Возвращает копию текущего QuerySet (или подкласса QuerySet). Это может быть полезно в ситуациях, когда вы хотите передать менеджер модели или набор QuerySet и выполнить дополнительную фильтрацию результата. После вызова all() на любом объекте у вас определенно будет набор QuerySet для работы.
Когда набор запросов QuerySet вычисляется, он обычно кэширует свои результаты. Если данные в базе данных могли измениться с момента вычисления набора QuerySet, вы можете получить обновленные результаты для того же запроса, вызвав all() на ранее вычисленном наборе QuerySet.
union()
-
union(*other_qs, all=False)
Использует оператор SQL UNION для объединения результатов двух или более наборов QuerySet.
>>> qs1.union(qs2, qs3)
Оператор UNION по умолчанию выбирает только уникальные значения. Для разрешения дублирующих значений используйте аргумент all=True.
union(), intersection(), и difference() возвращают экземпляры модели типа первого QuerySet, даже если аргументы — это наборы QuerySet других моделей. Передача разных моделей работает, пока список SELECT одинаков во всех наборах QuerySet (по крайней мере, типы, имена не важны, пока типы в одном и том же порядке). В таких случаях вы должны использовать имена столбцов из первого набора QuerySet в методах QuerySet применяемых к полученному набору QuerySet. Например:
>>> qs1 = Author.objects.values_list("name")
>>> qs2 = Entry.objects.values_list("headline")
>>> qs1.union(qs2).order_by("name")
END_OF_DOCUMENT_MARKER Кроме того, разрешены только LIMIT, OFFSET, COUNT(*), ORDER BY, и указание столбцов (т.е. срезы, count(), exists(), order_by() и values()/values_list()) на результирующем QuerySet. Кроме того, базы данных накладывают ограничения на разрешенные операции в объединенных запросах. Например, большинство баз данных не позволяют LIMIT или OFFSET в объединенных запросах.
intersection()
-
intersection(*other_qs)
Использует оператор SQL INTERSECT для возвращения общих элементов двух или более QuerySet. Например:
>>> qs1.intersection(qs2, qs3)
См. union() для некоторых ограничений.
difference()
-
difference(*other_qs)
Использует оператор SQL EXCEPT для сохранения только элементов, присутствующих в QuerySet, но отсутствующих в других QuerySet. Например:
>>> qs1.difference(qs2, qs3)
См. union() для некоторых ограничений.
select_related()
Возвращает QuerySet, который будет «следовать» за отношениями внешнего ключа, выбирая дополнительные данные связанных объектов при выполнении запроса. Это ускоряет производительность, что приводит к одному более сложному запросу, но означает, что последующее использование отношений внешнего ключа не потребует запросов к базе данных.
Следующие примеры иллюстрируют разницу между обычными запросами и запросами с select_related(). Вот стандартный запрос:
# Hits the database. e = Entry.objects.get(id=5) # Hits the database again to get the related Blog object. b = e.blog
А вот запрос с select_related:
# Hits the database.
e = Entry.objects.select_related("blog").get(id=5)
# Doesn't hit the database, because e.blog has been prepopulated
# in the previous query.
b = e.blog
Вы можете использовать select_related() с любым набором объектов запроса:
from django.utils import timezone
# Find all the blogs with entries scheduled to be published in the future.
blogs = set()
for e in Entry.objects.filter(pub_date__gt=timezone.now()).select_related("blog"):
# Without select_related(), this would make a database query for each
# loop iteration in order to fetch the related blog for each entry.
blogs.add(e.blog)
Порядок цепочки filter() и select_related() не важен. Эти наборы запросов эквивалентны:
Entry.objects.filter(pub_date__gt=timezone.now()).select_related("blog")
Entry.objects.select_related("blog").filter(pub_date__gt=timezone.now())
Вы можете следовать внешним ключам аналогичным образом, как при запросе к ним. Если у вас есть следующие модели:
from django.db import models
class City(models.Model):
# ...
pass
class Person(models.Model):
# ...
hometown = models.ForeignKey(
City,
on_delete=models.SET_NULL,
blank=True,
null=True,
)
class Book(models.Model):
# ...
author = models.ForeignKey(Person, on_delete=models.CASCADE)
… то вызов Book.objects.select_related('author__hometown').get(id=4) кэширует связанные Person и связанные City:
# Hits the database with joins to the author and hometown tables.
b = Book.objects.select_related("author__hometown").get(id=4)
p = b.author # Doesn't hit the database.
c = p.hometown # Doesn't hit the database.
# Without select_related()...
b = Book.objects.get(id=4) # Hits the database.
p = b.author # Hits the database.
c = p.hometown # Hits the database.
Вы можете ссылаться на любую ForeignKey или OneToOneField связь в списке полей, переданных в select_related().
Вы также можете обратиться к обратной стороне OneToOneField в списке полей, переданных в select_related — то есть вы можете проследить OneToOneField обратно к объекту, в котором определено поле. Вместо указания имени поля используйте related_name для поля в связанном объекте.
В некоторых ситуациях вам может потребоваться вызвать select_related() с большим количеством связанных объектов или когда вы не знаете всех отношений. В этих случаях можно вызвать select_related() без аргументов. Это позволит следовать всем не-NULL внешним ключам, которые могут быть найдены – значения NULL внешних ключей должны быть указаны. Это не рекомендуется в большинстве случаев, поскольку это, вероятно, сделает базовый запрос более сложным и вернет больше данных, чем это действительно необходимо.
Если вам нужно очистить список связанных полей, добавленных предыдущими вызовами select_related для QuerySet, вы можете передать None в качестве параметра:
>>> without_relations = queryset.select_related(None)
Цепочки вызовов select_related работают аналогично другим методам – т.е. select_related('foo', 'bar') эквивалентно select_related('foo').select_related('bar').
prefetch_related()
Возвращает QuerySet , который автоматически извлекает в одной партии связанные объекты для каждого из указанных запросов.
Это имеет сходную цель с select_related, поскольку оба предназначены для предотвращения наводнения запросов к базе данных при доступе к связанным объектам, но стратегия совершенно иная.
select_related работает путем создания SQL-соединения и включения полей связанного объекта в SELECT предложение. По этой причине select_related получает связанные объекты в одном запросе к базе данных. Однако, чтобы избежать гораздо большего набора результатов, который получился бы при соединении через отношение «многие», select_related ограничено отношениями с одним значением – внешним ключом и один-к-одному.
prefetch_related, с другой стороны, выполняет отдельный поиск для каждого отношения и выполняет «соединение» в Python. Это позволяет ему предварительно загрузить объекты многие-ко-многим и многие-к-одному, чего нельзя сделать с помощью select_related, в дополнение к отношениям внешний ключ и один-к-одному, которые поддерживаются select_related. Он также поддерживает предварительную загрузку GenericRelation и GenericForeignKey, однако, он должен быть ограничен однородным набором результатов. Например, предварительная загрузка объектов, на которые ссылается GenericForeignKey , поддерживается только в том случае, если запрос ограничен одним ContentType.
Например, предположим, у вас есть эти модели:
from django.db import models
class Topping(models.Model):
name = models.CharField(max_length=30)
class Pizza(models.Model):
name = models.CharField(max_length=50)
toppings = models.ManyToManyField(Topping)
def __str__(self):
return "%s (%s)" % (
self.name,
", ".join(topping.name for topping in self.toppings.all()),
)
и выполняется:
>>> Pizza.objects.all() ["Hawaiian (ham, pineapple)", "Seafood (prawns, smoked salmon)"...
Проблема в том, что каждый раз, когда Pizza.__str__() запрашивает self.toppings.all(), ему нужно обратиться к базе данных, поэтому Pizza.objects.all() будет выполнять запрос к таблице Toppings для каждого элемента в таблице Pizza QuerySet.
Мы можем сократить до двух запросов, используя prefetch_related:
>>> Pizza.objects.prefetch_related('toppings')
Это подразумевает self.toppings.all() для каждого Pizza; теперь каждый раз, когда вызывается self.toppings.all(), вместо того, чтобы обращаться к базе данных за элементами, он найдет их в кэше предварительной загрузки QuerySet, который был заполнен в одном запросе.
То есть, все соответствующие наполнители будут извлечены в одном запросе и использованы для составления QuerySets с предварительно заполненным кэшем соответствующих результатов; эти QuerySets затем используются в вызовах self.toppings.all().
Дополнительные запросы в prefetch_related() выполняются после того, как QuerySet начал оцениваться, и основной запрос был выполнен.
Если у вас есть итерируемый набор экземпляров модели, вы можете предварительно загрузить связанные атрибуты для этих экземпляров, используя функцию prefetch_related_objects().
Обратите внимание, что кэш результатов основного QuerySet и всех указанных связанных объектов затем будут полностью загружены в память. Это изменяет типичное поведение QuerySets, которые обычно пытаются избежать загрузки всех объектов в память до их необходимости, даже после выполнения запроса в базе данных.
Примечание
Помните, что, как и всегда с QuerySets, любые последующие связанные методы, которые подразумевают другой запрос к базе данных, игнорируют предварительно кэшированные результаты и извлекают данные с помощью нового запроса к базе данных. Таким образом, если вы напишете следующее:
>>> pizzas = Pizza.objects.prefetch_related('toppings')
>>> [list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]
…то тот факт, что pizza.toppings.all() был предварительно загружен, не поможет вам. prefetch_related('toppings') подразумевал pizza.toppings.all(), но pizza.toppings.filter() — это новый и другой запрос. Кэш предварительной загрузки здесь не поможет; на самом деле это снижает производительность, так как вы выполнили запрос к базе данных, который не использовали. Поэтому используйте эту функцию с осторожностью!
Также, если вы вызываете изменяющие базу данных методы add(), remove(), clear() или set() на related managers, любой кэш предварительной загрузки для отношения будет очищен.
Вы также можете использовать стандартную синтаксис объединения для связанных полей связанных полей. Предположим, у нас есть дополнительная модель к приведенному выше примеру:
class Restaurant(models.Model):
pizzas = models.ManyToManyField(Pizza, related_name="restaurants")
best_pizza = models.ForeignKey(
Pizza, related_name="championed_by", on_delete=models.CASCADE
)
Все следующие варианты допустимы:
>>> Restaurant.objects.prefetch_related("pizzas__toppings")
Это позволит предварительно загрузить все пиццы, принадлежащие ресторанам, и все начинки, принадлежащие этим пицзам. Это приведет к общему количеству запросов в базу данных – один для ресторанов, один для пицц и один для начинок.
>>> Restaurant.objects.prefetch_related("best_pizza__toppings")
Это позволит извлечь лучшую пиццу и все начинки для лучшей пиццы для каждого ресторана. Это будет выполнено в трех запросах к базе данных – один для ресторанов, один для «лучших пицц» и один для начинок.
Отношение best_pizza также можно получить, используя select_related, чтобы уменьшить количество запросов до 2:
>>> Restaurant.objects.select_related("best_pizza").prefetch_related("best_pizza__toppings")
Поскольку предварительная выборка выполняется после основного запроса (который включает соединения, необходимые для select_related), она может определить, что объекты best_pizza уже были извлечены, и пропустит повторное извлечение.
Цепное использование вызовов prefetch_related будет накапливать предварительно извлекаемые запросы. Чтобы очистить любое поведение prefetch_related, передайте None в качестве параметра:
>>> non_prefetched = qs.prefetch_related(None)
Следует отметить одно различие при использовании prefetch_related. Объекты, созданные запросом, могут быть общими для различных объектов, к которым они относятся, т.е. одна экземпляр модели Python может появляться более чем в одной точке дерева возвращаемых объектов. Это обычно происходит с отношениями внешнего ключа. Обычно это поведение не создает проблем и, на самом деле, экономит память и время процессора.
Хотя prefetch_related поддерживает предварительную выборку отношений GenericForeignKey, количество запросов зависит от данных. Поскольку GenericForeignKey может ссылаться на данные в нескольких таблицах, требуется по одному запросу на каждую таблицу, на которую ссылается 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;
Обратите внимание, что скобки, необходимые большинству СУБД для подзапросов, не требуются в
selectDjango. Также обратите внимание, что некоторые СУБД, такие как некоторые версии MySQL, не поддерживают подзапросы.В редких случаях вы можете передавать параметры в SQL-фрагменты в
extra(select=...). Для этого используйте параметрselect_params.Это сработает, например:
Blog.objects.extra( select={"a": "%s", "b": "%s"}, select_params=("one", "two"), )Если вам нужно использовать литеральную
%sвнутри строки запроса, используйте последовательность%%s. -
where/tablesВы можете определить явные SQL-
WHERE-запросы — возможно, для выполнения неявных соединений — с помощьюwhere. Вы можете вручную добавлять таблицы кFROM-запросу, используяtables.whereиtablesпринимают список строк. Всеwhereпараметры связываются с другими критериями поиска операцией «И».Пример:
Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
…примерно переводится на следующий SQL:
SELECT * FROM blog_entry WHERE (foo='a' OR bar='a') AND (baz='a')
Будьте осторожны при использовании параметра
tables, если вы указываете таблицы, которые уже используются в запросе. Когда вы добавляете дополнительные таблицы с помощью параметраtables, Django предполагает, что вы хотите включить эту таблицу еще раз, если она уже включена. Это создает проблему, так как имя таблицы будет затем иметь псевдоним. Если таблица появляется несколько раз в SQL-запросе, второй и последующие случаи должны использовать псевдонимы, чтобы база данных могла их отличить. Если вы ссылаетесь на дополнительную таблицу, добавленную в параметреwhere, это вызовет ошибки.Обычно вы будете добавлять только дополнительные таблицы, которые еще не появлялись в запросе. Однако, если возникает описанный выше случай, есть несколько решений. Во-первых, попробуйте обойтись без включения дополнительной таблицы и используйте ту, что уже есть в запросе. Если это невозможно, поместите вызов
extra()в начало построения набора результатов запроса, чтобы ваша таблица была первым использованием этой таблицы. Наконец, если ничего не помогает, посмотрите на сгенерированный запрос и перепишите добавлениеwhereс использованием псевдонима, присвоенного вашей дополнительной таблице. Псевдоним будет одинаковым каждый раз, когда вы создаёте набор результатов запроса таким же образом, поэтому вы можете полагаться на имя псевдонима, чтобы оно не изменилось. -
order_byЕсли вам нужно отсортировать полученный набор результатов запроса, используя некоторые из новых полей или таблиц, включённых с помощью
extra(), используйте параметрorder_by, чтобыextra(), и передайте последовательность строк. Эти строки должны быть полями модели (как в обычном методеorder_by()наборах результатов запроса), в форматеtable_name.column_nameили псевдонимом столбца, указанного в параметреselectдляextra().Например:
q = Entry.objects.extra(select={"is_recent": "pub_date > '2006-01-01'"}) q = q.extra(order_by=["-is_recent"])Это отсортирует все элементы, для которых
is_recentравно true, в начало набора результатов (Trueсортируется передFalseв порядке убывания).Кстати, это показывает, что вы можете делать несколько вызовов
extra()и он будет работать как ожидается (добавляя новые ограничения каждый раз). -
paramsУказанный выше параметр
whereможет использовать стандартные подстановки строк Python для базы данных —'%s'для указания параметров, которые база данных должна автоматически привести к каноническому виду. Аргументparams— это список любых дополнительных параметров, которые необходимо заменить.Пример:
Entry.objects.extra(where=["headline=%s"], params=["Lennon"])
Всегда используйте
paramsвместо встраивания значений непосредственно вwhere, потому чтоparamsгарантирует, что значения будут приведены к каноническому виду в соответствии с вашей конкретной базой данных. Например, кавычки будут правильно экранированы.Плохо:
Entry.objects.extra(where=["headline='Lennon'"])
Хорошо:
Entry.objects.extra(where=["headline=%s"], params=["Lennon"])
Предупреждение
Если вы выполняете запросы к MySQL, обратите внимание, что неявное приведение типов в MySQL может привести к неожиданным результатам при смешивании типов. Если вы запрашиваете столбец строкового типа, но с целочисленным значением, MySQL приведёт все значения в таблице к целочисленному типу перед выполнением сравнения. Например, если ваша таблица содержит значения 'abc', 'def' и вы запрашиваете WHERE mycolumn=0, обе строки будут соответствовать. Чтобы избежать этого, выполните правильное приведение типов перед использованием значения в запросе.
defer()
-
defer(*fields)
В некоторых сложных ситуациях моделирования данных ваши модели могут содержать много полей, некоторые из которых могут содержать много данных (например, текстовые поля) или требуют дорогостоящей обработки для преобразования в объекты Python. Если вы используете результаты набора результатов запроса в ситуации, когда вы не знаете, нужны ли вам определенные поля, когда вы изначально получаете данные, вы можете сказать Django, чтобы он не извлекал их из базы данных.
Это делается путём передачи имён полей, которые не нужно загружать, в defer():
Entry.objects.defer("headline", "body")
Набор результатов запроса, у которого есть отложенные поля, всё ещё вернёт экземпляры модели. Каждый отложенный атрибут будет извлечён из базы данных, если вы обратитесь к этому полю (по одному за раз, а не ко всем отложенным полям сразу).
Примечание
Отложенные поля не будут загружаться лениво из асинхронного кода. Вместо этого вы получите исключение 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(), ниже) предназначены только для продвинутых случаев использования. Они предоставляют оптимизацию для случаев, когда вы тщательно проанализировали свои запросы и точно понимаете, какая информация вам нужна, и измерили, что разница между возвращением необходимых вам полей и полным набором полей модели будет значительной.
Даже если вы считаете, что находитесь в ситуации продвинутого использования, используйте 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(), также является ошибкой.
Как и с 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 8.0.1+ поддерживает аргументы nowait, skip_locked, и of. Аргумент no_key поддерживается только в PostgreSQL.
Передача nowait=True, skip_locked=True, no_key=True, или of в select_for_update() с помощью бэкэндов баз данных, которые не поддерживают эти параметры, например, MySQL, вызывает NotSupportedError. Это предотвращает неожиданное блокирование кода.
Оценивание набора запросов с select_for_update() в режиме autocommit на бэкендах, которые поддерживают SELECT ... FOR UPDATE, является ошибкой TransactionManagementError, потому что строки в этом случае не блокируются. Если это было бы разрешено, это способствовало бы повреждению данных и могло быть легко вызвано кодом, который ожидает выполнения вне транзакции.
Использование select_for_update() на бэкендах, которые не поддерживают SELECT ... FOR UPDATE (например, SQLite), не повлияет. SELECT ... FOR UPDATE не будет добавлен в запрос, и ошибка не будет поднята, если select_for_update() используется в режиме autocommit.
Предупреждение
Хотя select_for_update() обычно не удаётся в режиме autocommit, так как TestCase автоматически включает каждую проверку в транзакцию, вызов select_for_update() в TestCase даже вне блока atomic() (возможно, неожиданно) пройдет без поднятия TransactionManagementError. Чтобы должным образом протестировать select_for_update() , вы должны использовать TransactionTestCase.
Определённые выражения могут не поддерживаться
PostgreSQL не поддерживает select_for_update() с Window выражениями.
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, y=2) from django.db.models import Q Model.objects.filter(Q(x=1) & Q(y=2))
Эквивалент SQL:
SELECT ... WHERE x=1 AND y=2
ИЛИ (|)
Объединяет два набора запросов с помощью оператора SQL OR.
Следующие выражения эквивалентны:
Model.objects.filter(x=1) | Model.objects.filter(y=2) from django.db.models import Q Model.objects.filter(Q(x=1) | Q(y=2))
Эквивалент SQL:
SELECT ... WHERE x=1 OR y=2
| — не коммутативная операция, так как могут генерироваться разные (хотя и эквивалентные) запросы.
ИСКЛЮЧАЮЩЕЕ ИЛИ (^)
Объединяет два набора запросов с помощью оператора SQL 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=(
(CASE WHEN x THEN 1 ELSE 0 END) +
(CASE WHEN y THEN 1 ELSE 0 END) +
...
(CASE WHEN z THEN 1 ELSE 0 END) +
)
Методы, не возвращающие QuerySet
Следующие методы QuerySet оценивают QuerySet и возвращают что-то кроме QuerySet.
Эти методы не используют кэш (см. Кэширование и QuerySet). Вместо этого они каждый раз обращаются к базе данных.
Поскольку эти методы оценивают QuerySet, они являются блокирующими вызовами, и поэтому их основные (синхронные) версии нельзя вызывать из асинхронного кода. По этой причине для каждого из них есть соответствующая асинхронная версия с префиксом a — например, вместо get(…) можно использовать await aget(…).
Обычно различий в поведении, кроме асинхронности, нет, но любые отличия указаны ниже рядом с каждым методом.
Были добавлены асинхронные версии каждого метода с префиксом a.
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.")
Метод aget() был добавлен.
create()
-
create(**kwargs)
-
acreate(*args, **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, так как первичные ключи должны быть уникальными. Будьте готовы обработать исключение, если вы используете первичные ключи, введённые вручную.
Метод acreate() был добавлен.
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 и вы хотите использовать его в качестве точного поиска в get_or_create(), используйте 'defaults__exact', как показано ниже:
Foo.objects.get_or_create(defaults__exact="bar", defaults={"defaults": "baz"})
Метод get_or_create() имеет аналогичное поведение с ошибками для create(), когда вы используете вручную заданные первичные ключи. Если объект нужно создать, а ключ уже существует в базе данных, будет вызвано исключение IntegrityError.
И наконец, несколько слов об использовании get_or_create() в представлениях Django. Пожалуйста, используйте его только в POST запросах, если у вас нет веской причины этого не делать. GET запросы не должны оказывать никакого влияния на данные. Вместо этого используйте POST всякий раз, когда запрос на страницу оказывает побочное влияние на ваши данные. Более подробную информацию см. в Безопасные методы в спецификации HTTP.
Предупреждение
Вы можете использовать get_or_create() через атрибуты ManyToManyField и обратные связи. В этом случае вы ограничите запросы в контексте этой связи. Это может привести к проблемам целостности, если вы не используете его последовательно.
Рассмотрим следующие модели:
class Chapter(models.Model):
title = models.CharField(max_length=255, unique=True)
class Book(models.Model):
title = models.CharField(max_length=256)
chapters = models.ManyToManyField(Chapter)
Вы можете использовать get_or_create() через поле глав chapters модели Book, но это только извлечёт данные в контексте этой книги:
>>> book = Book.objects.create(title="Ulysses") >>> book.chapters.get_or_create(title="Telemachus") (<Chapter: Telemachus>, True) >>> book.chapters.get_or_create(title="Telemachus") (<Chapter: Telemachus>, False) >>> Chapter.objects.create(title="Chapter 1") <Chapter: Chapter 1> >>> book.chapters.get_or_create(title="Chapter 1") # Raises IntegrityError
Это происходит потому, что он пытается получить или создать «Главу 1» через книгу «Улисс», но не может сделать ни то, ни другое: связь не может извлечь эту главу, потому что она не связана с этой книгой, а также не может создать её, потому что поле title должно быть уникальным.
Метод aget_or_create() был добавлен.
update_or_create()
-
update_or_create(defaults=None, **kwargs)
-
aupdate_or_create(defaults=None, **kwargs)
Асинхронная версия: aupdate_or_create()
Удобный метод для обновления объекта с заданными kwargs, создавая новый при необходимости. defaults — словарь пар (поле, значение), используемый для обновления объекта. Значения в defaults могут быть вызываемыми объектами.
Возвращает кортеж (object, created), где object — созданный или обновлённый объект, а created — булево значение, указывающее, был ли создан новый объект.
Метод update_or_create пытается извлечь объект из базы данных на основе заданного kwargs. Если совпадение найдено, он обновляет поля, переданные в словаре defaults.
Это предназначено как сокращение для шаблонного кода. Например:
defaults = {"first_name": "Bob"}
try:
obj = Person.objects.get(first_name="John", last_name="Lennon")
for key, value in defaults.items():
setattr(obj, key, value)
obj.save()
except Person.DoesNotExist:
new_values = {"first_name": "John", "last_name": "Lennon"}
new_values.update(defaults)
obj = Person(**new_values)
obj.save()
Эта схема становится довольно громоздкой по мере увеличения количества полей в модели. Приведённый выше пример можно переписать с использованием update_or_create() следующим образом:
obj, created = Person.objects.update_or_create(
first_name="John",
last_name="Lennon",
defaults={"first_name": "Bob"},
)
Подробное описание того, как решаются имена, переданные в kwargs, см. в get_or_create().
Как описано выше в get_or_create(), этот метод подвержен гонкам, которые могут привести к одновременной вставке нескольких строк, если уникальность не обеспечена на уровне базы данных.
Как и get_or_create() и create(), если вы используете вручную заданные первичные ключи и объект необходимо создать, но ключ уже существует в базе данных, возникает IntegrityError.
aupdate_or_create() метод был добавлен.
В более старых версиях update_or_create() не указывал update_fields при вызове Model.save().
bulk_create()
-
bulk_create(objs, batch_size=None, ignore_conflicts=False, update_conflicts=False, update_fields=None, unique_fields=None)
-
abulk_create(objs, batch_size=None, ignore_conflicts=False, update_conflicts=False, update_fields=None, unique_fields=None)
Асинхронная версия: abulk_create()
Этот метод эффективно вставляет предоставленный список объектов в базу данных (как правило, одной запросом, независимо от количества объектов) и возвращает созданные объекты в виде списка в том же порядке, что и предоставленном:
>>> objs = Entry.objects.bulk_create( ... [ ... Entry(headline="This is a test"), ... Entry(headline="This is only a test"), ... ] ... )
Однако это имеет ряд ограничений:
- Метод модели
save()не будет вызываться, и сигналыpre_saveиpost_saveне будут отправляться. - Он не работает с дочерними моделями в сценарии наследования с несколькими таблицами.
- Если первичный ключ модели — это
AutoField, то атрибут первичного ключа может быть извлечён только на определённых базах данных (в настоящее время PostgreSQL, MariaDB 10.5+ и SQLite 3.35+). На других базах данных он не будет установлен. - Он не работает с многозначными отношениями.
-
Он преобразует
objsв список, что полностью оцениваетobjsесли это генератор. Преобразование в список позволяет проверить все объекты, чтобы любые объекты с вручную заданным первичным ключом могли быть вставлены первыми. Если вы хотите вставлять объекты по частям, не оценивая весь генератор сразу, вы можете использовать этот метод, при условии, что у объектов нет вручную заданных первичных ключей:from itertools import islice batch_size = 100 objs = (Entry(headline="Test %s" % i) for i in range(1000)) while True: batch = list(islice(objs, batch_size)) if not batch: break Entry.objects.bulk_create(batch, batch_size)
Параметр batch_size управляет количеством объектов, создаваемых в одном запросе. По умолчанию все объекты создаются в одной группе, за исключением SQLite, где по умолчанию используется не более 999 переменных на запрос.
На базах данных, которые его поддерживают (все, кроме Oracle), установка параметра ignore_conflicts в True сообщает базе данных игнорировать ошибки вставки строк, которые нарушают ограничения, такие как дублирование уникальных значений.
На базах данных, которые его поддерживают (все, кроме Oracle и SQLite < 3.24), установка параметра update_conflicts в True, сообщает базе данных обновить update_fields при ошибке вставки строки из-за конфликтов. В PostgreSQL и SQLite помимо update_fields, должен быть предоставлен список unique_fields потенциально находящихся в конфликте.
Включение параметров ignore_conflicts или update_conflicts отключает установку первичного ключа для каждого экземпляра модели (если база данных это обычно поддерживает).
Предупреждение
В MySQL и MariaDB установка параметра ignore_conflicts в True превращает определённые типы ошибок, кроме дублирования ключа, в предупреждения. Даже в режиме жёстких ограничений. Например, неверные значения или нарушения неотсутствующих значений. Более подробную информацию см. в документации MySQL по ссылке и MariaDB по ссылке.
Параметры update_conflicts, update_fields, и unique_fields были добавлены для поддержки обновления полей при ошибках вставки строки из-за конфликтов.
abulk_create() метод был добавлен.
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, где есть ограничения на количество переменных, используемых в запросе.
abulk_update() метод был добавлен.
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() будет использовать эту длину, а не выполнять дополнительный запрос к базе данных.
acount() метод был добавлен.
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() пустой список, вы получите пустой словарь.
ain_bulk() метод был добавлен.
iterator()
-
iterator(chunk_size=None)
-
aiterator(chunk_size=None)
Асинхронная версия: aiterator()
Оценивает QuerySet (выполняя запрос) и возвращает итератор (см. PEP 234) по результатам или асинхронный итератор (см. PEP 492), если вы вызываете его асинхронную версию aiterator.
QuerySet обычно кэширует свои результаты внутри, чтобы повторные оценки не приводили к дополнительным запросам. В отличие от iterator(), который будет читать результаты напрямую, без кэширования на уровне QuerySet (внутренне, итератор по умолчанию вызывает iterator() и кэширует возвращаемое значение). Для QuerySet, который возвращает большое количество объектов, которые вам нужно получить только один раз, это может привести к улучшению производительности и значительному сокращению потребления памяти.
Обратите внимание, что использование iterator() на QuerySet, который уже был оценен, заставит его переоценить, повторив запрос.
iterator() совместим с предыдущими вызовами prefetch_related(), если chunk_size задан. Более высокие значения потребуют меньше запросов для выполнения предварительной выборки за счёт увеличения потребления памяти.
Примечание
aiterator() не совместим с предыдущими вызовами prefetch_related().
В некоторых базах данных (например, Oracle, SQLite) может быть ограничено максимальное количество терминов в предложении SQL IN. Следовательно, следует использовать значения, не превышающие это ограничение. (В частности, при предварительной выборке по двум или более отношениям chunk_size должно быть достаточно малым, чтобы ожидаемое количество результатов для каждого предварительно выбранного отношения всё ещё не превышало ограничение.)
До тех пор, пока QuerySet не предварительно выбирает связанные объекты, отсутствие значения для chunk_size приведет к тому, что Django будет использовать неявное значение по умолчанию 2000.
В зависимости от бэкенда базы данных, результаты запросов будут загружаться либо все сразу, либо передаваться по потокам из базы данных с использованием серверных курсоров.
Поддержка предварительной выборки связанных объектов была добавлена в iterator().
Метод aiterator() был добавлен.
Устарело начиная с версии 4.1: Использование iterator() на наборе запросов, который предварительно выбирает связанные объекты без предоставления chunk_size, устарело. В Django 5.0 будет возбуждено исключение.
С серверными курсорами
Oracle и PostgreSQL используют серверные курсоры для потоковой передачи результатов из базы данных без загрузки всего набора результатов в память.
Драйвер базы данных Oracle всегда использует серверные курсоры.
С серверными курсорами параметр chunk_size задаёт количество результатов для кэширования на уровне драйвера базы данных. Чтение больших блоков уменьшает количество обращений между драйвером базы данных и базой данных, но увеличивает потребление памяти.
В PostgreSQL серверные курсоры будут использоваться только тогда, когда настройка DISABLE_SERVER_SIDE_CURSORS имеет значение False. Прочитайте Пулы транзакций и серверные курсоры, если вы используете пул подключений, настроенный в режиме пула транзакций. Когда серверные курсоры отключены, поведение такое же, как у баз данных, которые не поддерживают серверные курсоры.
Без серверных курсоров
MySQL не поддерживает потоковую передачу результатов, поэтому драйвер базы данных Python загружает весь набор результатов в память. Затем адаптер базы данных преобразует набор результатов в объекты строк Python, используя метод fetchmany(), определённый в PEP 249.
SQLite может получать результаты в партиях, используя fetchmany(), но поскольку SQLite не обеспечивает изоляции между запросами в рамках подключения, будьте осторожны при записи в таблицу, по которой происходит итерация. Смотрите Изоляция при использовании QuerySet.iterator() для получения дополнительной информации.
Параметр chunk_size управляет размером партий, которые Django извлекает из драйвера базы данных. Большие партии уменьшают нагрузку на взаимодействие с драйвером базы данных, но немного увеличивают потребление памяти.
До тех пор, пока QuerySet не предварительно выбирает связанные объекты, отсутствие значения для chunk_size приведет к использованию Django неявного значения по умолчанию 2000, полученного из расчёта на почтовом списке psycopg:
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.
Если в метаданных вашей модели указано 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")
Метод alatest() был добавлен.
earliest()
-
earliest(*fields)
-
aearliest(*fields)
Асинхронная версия: aearliest()
В остальном работает так же, как latest(), за исключением изменения направления.
Метод aearliest() был добавлен.
first()
-
first()
-
afirst()
Асинхронная версия: afirst()
Возвращает первый объект, соответствующий набору запросов, или None, если соответствующего объекта нет. Если в наборе запросов нет определения сортировки, набор запросов автоматически сортируется по первичному ключу. Это может повлиять на результаты агрегации, как описано в Взаимодействии с 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
Метод afirst() был добавлен.
last()
-
last()
-
alast()
Асинхронная версия: alast()
Работает так же, как first(), но возвращает последний объект в наборе запросов.
Метод alast() был добавлен.
aggregate()
-
aggregate(*args, **kwargs)
-
aaggregate(*args, **kwargs)
Асинхронная версия: aaggregate()
Возвращает словарь агрегированных значений (средних, сумм и т. д.), вычисленных по QuerySet. Каждый аргумент для aggregate() определяет значение, которое будет включено в возвращаемый словарь.
Функции агрегирования, предоставляемые Django, описаны ниже в разделе Функции агрегирования. Поскольку агрегаты также являются выражениями запроса, вы можете объединять агрегаты с другими агрегатами или значениями для создания сложных агрегатов.
Агрегаты, заданные с помощью ключевых аргументов, будут использовать ключевое слово в качестве имени аннотации. Анонимные аргументы получат имя, сгенерированное на основе имени функции агрегирования и поля модели, которое агрегируется. Сложные агрегаты не могут использовать анонимные аргументы и должны указать ключевой аргумент в качестве псевдонима.
Например, при работе со статьями блога, вы можете захотеть узнать количество авторов, внесших вклад в статьи блога:
>>> from django.db.models import Count
>>> q = Blog.objects.aggregate(Count("entry"))
{'entry__count': 16}
Используя ключевой аргумент для указания функции агрегирования, вы можете контролировать имя возвращаемого значения агрегации:
>>> q = Blog.objects.aggregate(number_of_entries=Count("entry"))
{'number_of_entries': 16}
Для более подробного обсуждения агрегирования см. руководство по теме агрегирования.
aaggregate() был добавлен метод.
exists()
-
exists()
-
aexists()
Асинхронный вариант: aexists()
Возвращает True если QuerySet содержит какие-либо результаты, и False в противном случае. Эта попытка выполнить запрос наиболее простым и быстрым способом, но она действительно выполняет почти такой же запрос, как и обычный запрос QuerySet.
exists() полезен для поиска, относящегося к существованию каких-либо объектов в QuerySet, особенно в контексте большого QuerySet.
Чтобы определить, содержит ли набор запросов какие-либо элементы:
if some_queryset.exists():
print("There is at least one object in some_queryset")
Что будет быстрее, чем:
if some_queryset:
print("There is at least one object in some_queryset")
… но не на большой величине (поэтому для получения преимуществ эффективности требуется большой набор запросов).
Кроме того, если some_queryset еще не был оценен, но вы знаете, что он будет оценен в какой-то момент, то использование some_queryset.exists() выполнит больше работы в целом (один запрос для проверки существования плюс дополнительный запрос для последующего извлечения результатов), чем использование bool(some_queryset), которое извлекает результаты, а затем проверяет, были ли возвращены какие-либо.
aexists() был добавлен метод.
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) приведет к дополнительному запросу к базе данных, что, как правило, приведет к более медленной общей производительности.
acontains() был добавлен метод.
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()
aupdate() был добавлен метод.
Упорядоченный набор запросов
Использование 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})
По умолчанию, Django’s ForeignKey эмулирует SQL ограничение ON DELETE CASCADE — другими словами, любые объекты со ссылками на объекты, которые будут удалены, будут удалены вместе с ними. Например:
>>> blogs = Blog.objects.all()
# This will delete all Blogs and all of their Entry objects.
>>> blogs.delete()
(5, {'blog.Blog': 1, 'blog.Entry': 2, 'blog.Entry_authors': 2})
Это поведение каскадирования настраивается с помощью аргумента on_delete к ForeignKey.
Метод delete() выполняет массовое удаление и не вызывает никаких delete() методов для ваших моделей. Однако он генерирует сигналы pre_delete и post_delete для всех удаленных объектов (включая каскадные удаления).
Django необходимо загрузить объекты в память, чтобы отправлять сигналы и обрабатывать каскады. Однако, если нет каскадов и сигналов, Django может использовать быстрый путь и удалять объекты без загрузки в память. При больших удалениях это может значительно сократить использование памяти. Также может быть уменьшено количество выполняемых запросов.
ForeignKeys, которые установлены в on_delete DO_NOTHING не препятствуют использованию быстрого пути при удалении.
Обратите внимание, что запросы, сгенерированные при удалении объектов, являются деталями реализации и могут быть изменены.
adelete() метод был добавлен.
as_manager()
-
classmethod as_manager()
Метод класса, который возвращает экземпляр Manager с копией методов QuerySet. Подробнее см. Создание менеджера с методами QuerySet.
Обратите внимание, что в отличие от других записей в этом разделе, у него нет асинхронной версии, так как он не выполняет запрос.
explain()
-
explain(format=None, **options)
-
aexplain(format=None, **options)
Асинхронная версия: aexplain()
Возвращает строку с планом выполнения запроса, которая подробно описывает, как база данных выполнит запрос, включая используемые индексы или соединения. Знание этих деталей может помочь улучшить производительность медленных запросов.
Например, при использовании 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 запроса.
aexplain() метод был добавлен.
Field поиск
Поиск по полям — это способ указать основную часть SQL-%%CODE_BLOCK_1209%%%-запроса. Он указывается в качестве ключевых аргументов методам QuerySet, filter(), exclude() и get().
Для введения см. Документацию по моделям и запросам к базе данных.
Встроенные поиски Django перечислены ниже. Также возможно написать настраиваемые поиски для полей модели.
Для удобства, когда тип поиска не указан (как в Entry.objects.get(id=14)), предполагается, что тип поиска — exact.
exact
Точное совпадение. Если предоставленное значение для сравнения — None, оно будет интерпретировано как SQL NULL (подробнее см. isnull).
Примеры:
Entry.objects.get(id__exact=14) Entry.objects.get(id__exact=None)
SQL-эквиваленты:
SELECT ... WHERE id = 14; SELECT ... WHERE id IS NULL;
Сравнения MySQL
В MySQL настройка «кодировки» таблицы базы данных определяет, являются ли сравнения по exact чувствительными к регистру. Это настройка базы данных, а не Django. Можно настроить ваши MySQL таблицы для использования чувствительных к регистру сравнений, но есть некоторые компромиссы. Дополнительную информацию об этом см. в разделе кодировки в документации баз данных.
iexact
Нечувствительное к регистру точное совпадение. Если предоставленное значение для сравнения — None, оно будет интерпретировано как SQL NULL (подробнее см. isnull).
Пример:
Blog.objects.get(name__iexact="beatles blog") Blog.objects.get(name__iexact=None)
SQL-эквиваленты:
SELECT ... WHERE name ILIKE 'beatles blog'; SELECT ... WHERE name IS NULL;
Обратите внимание, что первый запрос будет соответствовать 'Beatles Blog', 'beatles blog', 'BeAtLes BLoG', и т.д.
Пользователи SQLite
При использовании бэкенда SQLite и не-ASCII строк помните заметку о сравнении строк. SQLite не выполняет нечувствительного к регистру сопоставления для не-ASCII строк.
contains
Чувствительное к регистру проверка наличия подстроки.
Пример:
Entry.objects.get(headline__contains="Lennon")
SQL-эквивалент:
SELECT ... WHERE headline LIKE '%Lennon%';
Обратите внимание, что это будет соответствовать заголовку 'Lennon honored today', но не 'lennon
honored today'.
Пользователи SQLite
SQLite не поддерживает чувствительные к регистру запросы LIKE; contains действует как icontains для SQLite. Смотрите заметку о базе данных для получения дополнительной информации.
icontains
Нечувствительное к регистру проверка наличия подстроки.
Пример:
Entry.objects.get(headline__icontains="Lennon")
SQL-эквивалент:
SELECT ... WHERE headline ILIKE '%Lennon%';
Пользователи SQLite
При использовании бэкенда SQLite и не-ASCII строк помните заметку о сравнении строк.
in
В заданном итерируемом объекте; часто список, кортеж или набор запросов. Это не распространённый случай, но строки (являясь итерируемыми) принимаются.
Примеры:
Entry.objects.filter(id__in=[1, 3, 4]) Entry.objects.filter(headline__in="abc")
SQL-эквиваленты:
SELECT ... WHERE id IN (1, 3, 4);
SELECT ... WHERE headline IN ('a', 'b', 'c');
Можно также использовать набор запросов для динамической оценки списка значений вместо предоставления списка литеральных значений:
inner_qs = Blog.objects.filter(name__contains="Cheddar") entries = Entry.objects.filter(blog__in=inner_qs)
Этот набор запросов будет вычислен как подзапрос:
SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')
Если вы передаете QuerySet, полученный из values() или values_list() в качестве значения для поиска по полю __in, убедитесь, что в результате извлекается только одно поле. Например, это будет работать (фильтрация по названиям блогов):
inner_qs = Blog.objects.filter(name__contains="Ch").values("name")
entries = Entry.objects.filter(blog__name__in=inner_qs)
Этот пример вызовет исключение, поскольку внутренний запрос пытается извлечь два значения полей, тогда как ожидается только одно:
# Bad code! Will raise a TypeError.
inner_qs = Blog.objects.filter(name__contains="Ch").values("name", "id")
entries = Entry.objects.filter(blog__name__in=inner_qs)
Соображения производительности
Будьте осторожны при использовании вложенных запросов и понимайте характеристики производительности вашего сервера базы данных (если сомневаетесь, сделайте тестирование производительности!). Некоторые бэкэнды баз данных, в частности MySQL, не очень хорошо оптимизируют вложенные запросы. В этих случаях эффективнее извлечь список значений и затем передать его во второй запрос. То есть, выполните два запроса вместо одного:
values = Blog.objects.filter(name__contains="Cheddar").values_list("pk", flat=True)
entries = Entry.objects.filter(blog__in=list(values))
Обратите внимание на вызов list() вокруг Blog QuerySet для принудительного выполнения первого запроса. Без него вложенный запрос будет выполнен, потому что наборы запросов ленивые.
gt
Больше чем.
Пример:
Entry.objects.filter(id__gt=4)
SQL-эквивалент:
SELECT ... WHERE id > 4;
gte
Больше или равно.
lt
Меньше чем.
lte
Меньше или равно.
startswith
Чувствительное к регистру начинается с.
Пример:
Entry.objects.filter(headline__startswith="Lennon")
SQL-эквивалент:
SELECT ... WHERE headline LIKE 'Lennon%';
SQLite не поддерживает чувствительные к регистру запросы LIKE; startswith действует как istartswith для SQLite.
istartswith
Нечувствительное к регистру начинается с.
Пример:
Entry.objects.filter(headline__istartswith="Lennon")
SQL-эквивалент:
SELECT ... WHERE headline ILIKE 'Lennon%';
Пользователи SQLite
При использовании бэкенда SQLite и не-ASCII строк помните заметку о сравнении строк.
endswith
Чувствительное к регистру заканчивается на.
Пример:
Entry.objects.filter(headline__endswith="Lennon")
SQL-эквивалент:
SELECT ... WHERE headline LIKE '%Lennon';
Пользователи SQLite
SQLite не поддерживает регистрозависимые LIKE операторы; endswith работает как iendswith для SQLite. Для получения дополнительной информации обратитесь к документации заметки по базе данных.
iendswith
Независимый от регистра поиск по окончанию.
Пример:
Entry.objects.filter(headline__iendswith="Lennon")
Эквивалент SQL:
SELECT ... WHERE headline ILIKE '%Lennon'
Пользователи SQLite
При использовании SQLite-бэкенда и строк, не являющихся ASCII, учитывайте заметки по базе данных о сравнении строк.
range
Тест диапазона (включительно).
Пример:
import datetime start_date = datetime.date(2005, 1, 1) end_date = datetime.date(2005, 3, 31) Entry.objects.filter(pub_date__range=(start_date, end_date))
Эквивалент SQL:
SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';
Вы можете использовать range везде, где в SQL можно использовать BETWEEN, — для дат, чисел и даже символов.
Предупреждение
Фильтрация DateTimeField по датам не включит записи на последний день, потому что границы интерпретируются как «0:00 в указанную дату». Если pub_date был DateTimeField, вышеприведённое выражение будет преобразовано в следующий SQL:
SELECT ... WHERE pub_date BETWEEN '2005-01-01 00:00:00' and '2005-03-31 00:00:00';
Как правило, вы не можете смешивать даты и 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
Для полей даты и 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
Для полей даты и datetime точное совпадение года по ISO 8601. Разрешает цепочку дополнительных поисков по полям. Принимает целое число года.
Пример:
Entry.objects.filter(pub_date__iso_year=2005) Entry.objects.filter(pub_date__iso_year__gte=2005)
(Точный синтаксис SQL зависит от каждой СУБД.)
Когда USE_TZ имеет значение True, поля datetime преобразуются в текущую часовую зону перед фильтрацией. Для этого требуются определения часовых поясов в базе данных.
month
Для полей даты и datetime точное совпадение месяца. Разрешает цепочку дополнительных поисков по полям. Принимает целое число от 1 (январь) до 12 (декабрь).
Пример:
Entry.objects.filter(pub_date__month=12) Entry.objects.filter(pub_date__month__gte=6)
Эквивалент SQL:
SELECT ... WHERE EXTRACT('month' FROM pub_date) = '12';
SELECT ... WHERE EXTRACT('month' FROM pub_date) >= '6';
(Точный синтаксис SQL зависит от каждой СУБД.)
Когда USE_TZ имеет значение True, поля datetime преобразуются в текущую часовую зону перед фильтрацией. Для этого требуются определения часовых поясов в базе данных.
day
Для полей даты и datetime точное совпадение дня. Разрешает цепочку дополнительных поисков по полям. Принимает целое число дня.
Пример:
Entry.objects.filter(pub_date__day=3) Entry.objects.filter(pub_date__day__gte=3)
Эквивалент SQL:
SELECT ... WHERE EXTRACT('day' FROM pub_date) = '3';
SELECT ... WHERE EXTRACT('day' FROM pub_date) >= '3';
(Точный синтаксис SQL зависит от каждой СУБД.)
Это соответствует любой записи с pub_date на третье число месяца, например, 3 января, 3 июля и т.д.
Когда USE_TZ имеет значение True, поля datetime преобразуются в текущую часовую зону перед фильтрацией. Для этого требуются определения часовых поясов в базе данных.
week
Для полей даты и datetime возвращает номер недели (1-52 или 53) в соответствии с ISO-8601, т.е. недели начинаются с понедельника, а первая неделя содержит первый четверг года.
Пример:
Entry.objects.filter(pub_date__week=52) Entry.objects.filter(pub_date__week__gte=32, pub_date__week__lte=38)
(Эквивалентный фрагмент кода SQL не включён, так как реализация соответствующего запроса варьируется в различных СУБД.)
Когда USE_TZ имеет значение True, поля datetime преобразуются в текущую часовую зону перед фильтрацией. Для этого требуются определения часовых поясов в базе данных.
week_day
Для полей даты и datetime совпадение «дня недели». Разрешает цепочку дополнительных поисков по полям.
Принимает целое число, представляющее день недели от 1 (воскресенье) до 7 (суббота).
Пример:
Entry.objects.filter(pub_date__week_day=2) Entry.objects.filter(pub_date__week_day__gte=2)
(Эквивалентный фрагмент кода SQL не включён, так как реализация соответствующего запроса варьируется в различных СУБД.)
Это соответствует любой записи с pub_date, которая приходится на понедельник (день 2 недели), независимо от месяца или года, в котором это происходит. Дни недели индексируются так, что 1 — воскресенье, а 7 — суббота.
Когда USE_TZ имеет значение True, поля datetime преобразуются в текущую часовую зону перед фильтрацией. Для этого требуются определения часовых поясов в базе данных.
iso_week_day
Для полей даты и datetime точное совпадение дня недели по ISO 8601. Разрешает цепочку дополнительных поисков по полям.
Принимает целое число, представляющее день недели от 1 (понедельник) до 7 (воскресенье).
Пример:
Entry.objects.filter(pub_date__iso_week_day=1) Entry.objects.filter(pub_date__iso_week_day__gte=1)
(Эквивалентный фрагмент кода SQL не включён, так как реализация соответствующего запроса варьируется в различных СУБД.)
Это соответствует любой записи с pub_date, которая приходится на понедельник (день 1 недели), независимо от месяца или года, в котором это происходит. Дни недели индексируются так, что 1 — понедельник, а 7 — воскресенье.
Когда USE_TZ имеет значение True, поля datetime преобразуются в текущую часовую зону перед фильтрацией. Для этого требуются определения часовых поясов в базе данных.
quarter
Для полей даты и datetime совпадение «четверти года». Разрешает цепочку дополнительных поисков по полям. Принимает целое число от 1 до 4, представляющее четверть года.
Пример поиска записей во втором квартале (с 1 апреля по 30 июня):
Entry.objects.filter(pub_date__quarter=2)
(Эквивалентный фрагмент кода SQL не включён, так как реализация соответствующего запроса варьируется в различных СУБД.)
Когда USE_TZ имеет значение True, поля datetime преобразуются в текущую часовую зону перед фильтрацией. Для этого требуются определения часовых поясов в базе данных.
time
Для полей datetime значение преобразуется к времени. Разрешает цепочку дополнительных поисков по полям. Принимает значение datetime.time.
Пример:
Entry.objects.filter(pub_date__time=datetime.time(14, 30)) Entry.objects.filter(pub_date__time__range=(datetime.time(8), datetime.time(17)))
(Эквивалентный фрагмент кода SQL не включён, так как реализация соответствующего запроса варьируется в различных СУБД.)
Когда USE_TZ имеет значение True, поля преобразуются в текущую часовую зону перед фильтрацией. Для этого требуются определения часовых поясов в базе данных.
hour
Для полей datetime и time точное совпадение часа. Разрешает цепочку дополнительных поисков по полям. Принимает целое число от 0 до 23.
Пример:
Event.objects.filter(timestamp__hour=23) Event.objects.filter(time__hour=5) Event.objects.filter(timestamp__hour__gte=12)
Эквивалент SQL:
SELECT ... WHERE EXTRACT('hour' FROM timestamp) = '23';
SELECT ... WHERE EXTRACT('hour' FROM time) = '5';
SELECT ... WHERE EXTRACT('hour' FROM timestamp) >= '12';
(Точный синтаксис SQL зависит от каждой СУБД.)
Когда USE_TZ имеет значение True, поля datetime преобразуются в текущую часовую зону перед фильтрацией. Для этого требуются определения часовых поясов в базе данных.
minute
Для полей datetime и time точное совпадение минуты. Разрешает цепочку дополнительных поисков по полям. Принимает целое число от 0 до 59.
Пример:
Event.objects.filter(timestamp__minute=29) Event.objects.filter(time__minute=46) Event.objects.filter(timestamp__minute__gte=29)
Эквивалент SQL:
SELECT ... WHERE EXTRACT('minute' FROM timestamp) = '29';
SELECT ... WHERE EXTRACT('minute' FROM time) = '46';
SELECT ... WHERE EXTRACT('minute' FROM timestamp) >= '29';
(Точный синтаксис SQL зависит от каждой СУБД.)
Когда USE_TZ имеет значение True, поля datetime преобразуются в текущую часовую зону перед фильтрацией. Для этого требуются определения часовых поясов в базе данных.
second
Для полей datetime и time точное совпадение секунды. Разрешает цепочку дополнительных поисков по полям. Принимает целое число от 0 до 59.
Пример:
Event.objects.filter(timestamp__second=31) Event.objects.filter(time__second=2) Event.objects.filter(timestamp__second__gte=31)
Эквивалент SQL:
SELECT ... WHERE EXTRACT('second' FROM timestamp) = '31';
SELECT ... WHERE EXTRACT('second' FROM time) = '2';
SELECT ... WHERE EXTRACT('second' FROM timestamp) >= '31';
(Точный синтаксис SQL зависит от каждой СУБД.)
Когда USE_TZ равно True, поля datetime преобразуются в текущую временную зону перед фильтрацией. Это требует определений временных зон в базе данных.
isnull
Принимает либо True , либо False, что соответствует SQL-запросам IS NULL и IS NOT NULL соответственно.
Пример:
Entry.objects.filter(pub_date__isnull=True)
Эквивалент в SQL:
SELECT ... WHERE pub_date IS NULL;
regex
Сопоставление с регулярным выражением, чувствительным к регистру.
Синтаксис регулярного выражения соответствует синтаксису используемого бэкенда базы данных. В случае SQLite, который не имеет встроенной поддержки регулярных выражений, эта функция предоставляется с помощью пользовательской (Python) функции REGEXP, и синтаксис регулярного выражения, следовательно, соответствует синтаксису модуля Python re.
Пример:
Entry.objects.get(title__regex=r"^(An?|The) +")
Эквиваленты в SQL:
SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL SELECT ... WHERE REGEXP_LIKE(title, '^(An?|The) +', 'c'); -- Oracle SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite
Рекомендуется использовать сырые строки (например, r'foo' вместо 'foo') для передачи синтаксиса регулярного выражения.
iregex
Сопоставление с регулярным выражением, нечувствительным к регистру.
Пример:
Entry.objects.get(title__iregex=r"^(an?|the) +")
Эквиваленты в SQL:
SELECT ... WHERE title REGEXP '^(an?|the) +'; -- MySQL SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'i'); -- Oracle SELECT ... WHERE title ~* '^(an?|the) +'; -- PostgreSQL SELECT ... WHERE title REGEXP '(?i)^(an?|the) +'; -- SQLite
Функции агрегации
Django предоставляет следующие функции агрегации в модуле django.db.models. Подробности о том, как использовать эти функции агрегации, см. в руководстве по агрегации. Чтобы узнать, как создавать свои агрегации, см. документацию Aggregate.
Предупреждение
SQLite не поддерживает агрегацию по полям даты/времени без дополнительных настроек. Это связано с отсутствием собственных полей даты/времени в SQLite, и Django в настоящее время эмулирует эти возможности с помощью текстового поля. Попытки использовать агрегацию по полям даты/времени в SQLite приведут к ошибке NotSupportedError.
Примечание
Функции агрегации возвращают None, когда используются с пустым QuerySet. Например, функция агрегации Sum возвращает None, а не 0, если QuerySet не содержит записей. Для возвращения другого значения вместо этого передайте значение в аргумент 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) -
Возвращает среднее значение заданного выражения, которое должно быть числовым, если не указано другое
output_field.- Значение по умолчанию:
<field>__avg - Тип возвращаемого значения:
floatесли входное значениеint, в противном случае такой же, как у входного поля, илиoutput_fieldесли указано.
-
distinct -
Необязательно. Если
distinct=True,Avgвозвращает среднее значение уникальных значений. Это эквивалент SQLAVG(DISTINCT <field>). Значение по умолчаниюFalse.
- Значение по умолчанию:
Count
-
class Count(expression, distinct=False, filter=None, **extra) -
Возвращает количество объектов, связанных через предоставленное выражение.
- Значение по умолчанию:
<field>__count - Тип возвращаемого значения:
int
-
distinct -
Необязательно. Если
distinct=True, подсчет будет включать только уникальные экземпляры. Это эквивалент SQLCOUNT(DISTINCT <field>). Значение по умолчаниюFalse.
Примечание
Аргумент
defaultне поддерживается. - Значение по умолчанию:
Max
-
class Max(expression, output_field=None, filter=None, default=None, **extra) -
Возвращает максимальное значение заданного выражения.
- Значение по умолчанию:
<field>__max - Тип возвращаемого значения: такой же, как у входного поля, или
output_fieldесли указано.
- Значение по умолчанию:
Min
-
class Min(expression, output_field=None, filter=None, default=None, **extra) -
Возвращает минимальное значение заданного выражения.
- Значение по умолчанию:
<field>__min - Тип возвращаемого значения: такой же, как у входного поля, или
output_fieldесли указано.
- Значение по умолчанию:
StdDev
-
class StdDev(expression, output_field=None, sample=False, filter=None, default=None, **extra) -
Возвращает стандартное отклонение данных в заданном выражении.
- Значение по умолчанию:
<field>__stddev - Тип возвращаемого значения:
floatесли входное значениеint, в противном случае такой же, как у входного поля, илиoutput_fieldесли указано.
-
sample -
Необязательно. По умолчанию,
StdDevвозвращает стандартное отклонение генеральной совокупности. Однако, еслиsample=True, возвращаемое значение будет стандартным отклонением выборки.
- Значение по умолчанию:
Sum
-
class Sum(expression, output_field=None, distinct=False, filter=None, default=None, **extra) -
Вычисляет сумму всех значений заданного выражения.
- Значение по умолчанию:
<field>__sum - Тип возвращаемого значения: такой же, как у входного поля, или
output_fieldесли указано.
-
distinct -
Необязательно. Если
distinct=True,Sumвозвращает сумму уникальных значений. Это эквивалент SQLSUM(DISTINCT <field>). Значение по умолчаниюFalse.
- Значение по умолчанию:
Variance
-
class Variance(expression, output_field=None, sample=False, filter=None, default=None, **extra) -
Возвращает дисперсию данных в заданном выражении.
- Значение по умолчанию:
<field>__variance - Тип возвращаемого значения:
floatесли входное значениеint, в противном случае такой же, как у входного поля, илиoutput_fieldесли указано.
-
sample -
Необязательно. По умолчанию,
Varianceвозвращает дисперсию генеральной совокупности. Однако, еслиsample=True, возвращаемое значение будет дисперсией выборки.
- Значение по умолчанию:
Инструменты для работы с запросами
Q() объекты
-
class Q
Объект Q() представляет SQL-условие, которое можно использовать в операциях, связанных с базой данных. Он похож на то, как объект F() представляет значение поля модели или аннотации. Они позволяют определять и повторно использовать условия и комбинировать их с помощью операторов, таких как | (OR), & (AND) и ^ (XOR). См. Сложные запросы с Q-объектами.
Добавлена поддержка оператора ^ (XOR).
Prefetch() объекты
-
class Prefetch(lookup, queryset=None, to_attr=None)
Объект Prefetch() можно использовать для управления операциями prefetch_related().
Аргумент lookup описывает отношения для следования и работает так же, как строковые запросы, переданные в prefetch_related(). Например:
>>> from django.db.models import Prefetch
>>> Question.objects.prefetch_related(Prefetch('choice_set')).get().choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
# This will only execute two queries regardless of the number of Question
# and Choice objects.
>>> Question.objects.prefetch_related(Prefetch('choice_set'))
<QuerySet [<Question: What's up?>]>
Аргумент queryset задаёт базовый QuerySet для заданного поиска. Это полезно для дальнейшей фильтрации операции предварительной выборки или для вызова select_related() из предварительно выбранной связи, тем самым ещё больше уменьшая количество запросов:
>>> voted_choices = Choice.objects.filter(votes__gt=0)
>>> voted_choices
<QuerySet [<Choice: The sky>]>
>>> prefetch = Prefetch('choice_set', queryset=voted_choices)
>>> Question.objects.prefetch_related(prefetch).get().choice_set.all()
<QuerySet [<Choice: The sky>]>
Аргумент to_attr устанавливает результат операции предварительной выборки в пользовательское свойство:
>>> prefetch = Prefetch('choice_set', queryset=voted_choices, to_attr='voted_choices')
>>> Question.objects.prefetch_related(prefetch).get().voted_choices
[<Choice: The sky>]
>>> Question.objects.prefetch_related(prefetch).get().choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
Примечание
При использовании to_attr результат предварительной выборки хранится в списке. Это может значительно ускорить работу по сравнению с традиционными вызовами prefetch_related, которые хранят кэшированный результат внутри экземпляра QuerySet.
prefetch_related_objects()
Предварительно выбирает указанные поля в итерируемом наборе экземпляров модели. Это полезно в коде, который получает список экземпляров моделей, в отличие от QuerySet; например, при извлечении моделей из кэша или при их ручном создании.
Передайте итерируемый набор экземпляров модели (все должны быть одного класса) и поля или объекты Prefetch, которые вы хотите предварительно выбрать. Например:
>>> from django.db.models import prefetch_related_objects >>> restaurants = fetch_top_restaurants_from_cache() # A list of Restaurants >>> prefetch_related_objects(restaurants, "pizzas__toppings")
При использовании нескольких баз данных с prefetch_related_objects, запрос предварительной выборки будет использовать базу данных, связанную с экземпляром модели. Это можно переопределить, используя пользовательский набор запросов в связанном поиске.
FilteredRelation()
-
class FilteredRelation(relation_name, *, condition=Q()) -
-
relation_name -
Имя поля, по которому вы хотите отфильтровать связь.
-
condition -
Объект
Q, который управляет фильтрацией.
-
FilteredRelation используется с annotate() для создания условия ON, когда выполняется JOIN. Он не действует на стандартную связь, а на имя аннотации (pizzas_vegetarian в примере ниже).
Например, чтобы найти рестораны, у которых есть вегетарианские пиццы с 'mozzarella' в названии:
>>> from django.db.models import FilteredRelation, Q >>> Restaurant.objects.annotate( ... pizzas_vegetarian=FilteredRelation( ... "pizzas", ... condition=Q(pizzas__vegetarian=True), ... ), ... ).filter(pizzas_vegetarian__name__icontains="mozzarella")
Если пицц много, этот набор запросов работает быстрее, чем:
>>> Restaurant.objects.filter( ... pizzas__vegetarian=True, ... pizzas__name__icontains="mozzarella", ... )
потому что фильтрация в WHERE условии первого набора запросов будет работать только с вегетарианскими пиццами.
FilteredRelation не поддерживает:
-
QuerySet.only()иprefetch_related(). -
GenericForeignKeyунаследованное от родительской модели.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/4.2/ref/models/querysets/