Справочник API QuerySet
В этом документе описаны подробности API QuerySet. Он основан на материалах, представленных в руководствах по моделям и запросам к базе данных, поэтому вам, вероятно, стоит прочитать и понять эти документы перед чтением этого.
На протяжении всего этого справочника мы будем использовать примеры моделей блогов моделей блога, представленные в руководстве по запросам к базе данных.
Когда QuerySet оцениваются
Внутри QuerySet может быть построен, отфильтрован, разбит на фрагменты и вообще передан без фактического обращения к базе данных. Никакой активности базы данных не происходит до тех пор, пока вы не сделаете что-то для оценки queryset.
Вы можете оценить QuerySet следующим образом:
-
Итерация.
QuerySetявляется итерируемым, и выполняет свой запрос к базе данных в первый раз, когда вы перебираете его. Например, это выведет заголовок всех записей в базе данных:for e in Entry.objects.all(): print(e.headline)Примечание: Не используйте этот метод, если вам нужно только определить, существует ли хотя бы один результат. Более эффективно использовать
exists(). -
Асинхронная итерация.
QuerySetтакже можно перебирать с помощьюasync for:async for e in Entry.objects.all(): results.append(e)Синхронные и асинхронные итераторы QuerySet используют один и тот же кэш.
-
Фрагментация. Как объяснено в Ограничение QuerySet,
QuerySetможно разбить на фрагменты, используя синтаксис фрагментации массивов Python. Разбитие на фрагменты неоцененногоQuerySetобычно возвращает другой неоцененныйQuerySet, но Django выполнит запрос к базе данных, если вы используете параметр «шаг» синтаксиса среза, и вернёт список. Разбитие на фрагменты уже оцененногоQuerySetтакже возвращает список.Также обратите внимание, что, хотя фрагментация неоцененного
QuerySetвозвращает другой неоцененныйQuerySet, дальнейшее изменение его (например, добавление дополнительных фильтров или изменение сортировки) запрещено, так как это плохо преобразуется в SQL и не будет иметь чёткого смысла. - Сериализация/Кэширование. Подробности процесса сериализации QuerySet см. в следующем разделе. Важно для этого раздела, что результаты считываются из базы данных.
-
repr().
QuerySetоценивается при вызовеrepr()на нём. Это для удобства в интерактивном интерпретаторе Python, чтобы вы сразу видели результаты при интерактивном использовании API. -
len().
QuerySetоценивается при вызовеlen()на нём. Это, как можно предположить, возвращает длину списка результатов.Примечание: Если вам нужно только определить количество записей в наборе (и вам не нужны сами объекты), гораздо эффективнее обработать счётчик на уровне базы данных с помощью SQL
SELECT COUNT(*). Django предоставляет методcount()именно для этого. -
list(). Вынудительно оцените
QuerySet, вызвавlist()на нём. Например:entry_list = list(Entry.objects.all())
-
bool(). Тестирование
QuerySetв контексте булева значения, таком как использованиеbool(),or,andили оператораif, вызовет выполнение запроса. Если существует хотя бы один результат, тоQuerySetбудетTrue, в противном случаеFalse. Например:if Entry.objects.filter(headline="Test"): print("There is at least one Entry with the headline Test")Примечание: Если вам нужно только определить, существует ли хотя бы один результат (и вам не нужны сами объекты), то более эффективно использовать
exists().
Сериализация QuerySet
Если вы pickle QuerySet, это приведет к загрузке всех результатов в память перед сериализацией. Сериализация обычно используется как предыстория для кэширования, и когда кэшированный queryset перезагружается, вы хотите, чтобы результаты уже были доступны и готовы к использованию (считывание из базы данных может занять некоторое время, что разрушает цель кэширования). Это означает, что когда вы десериализуете QuerySet, он содержит результаты на момент сериализации, а не те, что в настоящее время находятся в базе данных.
Если вы хотите сериализовать только необходимую информацию для регенерации 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)[source] -
Обычно при взаимодействии с
QuerySetвы будете использовать его, цепочкой фильтров. Для этого большинство методовQuerySetвозвращают новые querysets. Эти методы подробно описаны позже в этом разделе.Класс
QuerySetимеет следующие публичные атрибуты, которые вы можете использовать для интроспекции:-
ordered[source] -
TrueеслиQuerySetотсортирован — т.е. имеет условиеorder_by()или по умолчанию, или порядок сортировки модели.Falseв противном случае.
-
db[source] -
База данных, которая будет использоваться, если запрос будет выполнен сейчас.
Примечание
Параметр
queryдляQuerySetсуществует для того, чтобы специализированные подклассы запросов могли восстановить внутреннее состояние запроса. Значение параметра — непрозрачное представление этого состояния запроса и не является частью публичного API. -
Методы, возвращающие новые QuerySet
Django предоставляет ряд методов уточнения QuerySet, которые изменяют либо типы результатов, возвращаемых QuerySet, либо способ выполнения SQL-запроса.
Примечание
Эти методы не выполняют запросов к базе данных, поэтому их безопасно использовать в асинхронном коде и у них нет отдельных асинхронных версий.
filter()
-
filter(*args, **kwargs)
Возвращает новый QuerySet, содержащий объекты, которые соответствуют заданным параметрам поиска.
Параметры поиска (**kwargs) должны быть в формате, описанном в Поисках по полям ниже. Несколько параметров объединяются через AND в базовом SQL-запросе.
Если вам нужно выполнить более сложные запросы (например, запросы с OR выражениями), вы можете использовать Q objects (*args).
exclude()
-
exclude(*args, **kwargs)
Возвращает новый QuerySet, содержащий объекты, которые не соответствуют заданным параметрам поиска.
Параметры поиска (**kwargs) должны быть в формате, описанном в Поисках по полям ниже. Несколько параметров объединяются через AND в базовом SQL-запросе, и всё это заключено в NOT().
В этом примере исключаются все записи, у которых pub_date позже 2005-01-03 И у которых headline равно «Hello»:
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline="Hello")
В терминах SQL это эквивалентно:
SELECT ... WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')
Этот пример исключает все записи, у которых pub_date позже 2005-01-03 ИЛИ заголовок которых «Hello»:
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline="Hello")
В терминах SQL это эквивалентно:
SELECT ... WHERE NOT pub_date > '2005-1-3' AND NOT headline = 'Hello'
Обратите внимание, что второй пример более ограничен.
Если вам нужно выполнить более сложные запросы (например, запросы с OR операторами), вы можете использовать Q objects (*args).
annotate()
-
annotate(*args, **kwargs)
Добавляет к каждому объекту в QuerySet предоставленный список выражений запроса. Выражение может быть простым значением, ссылкой на поле в модели (или любой связанной модели), или агрегационной функцией (среднее значение, сумма и т. д.), которая была вычислена по объектам, связанным с объектами в QuerySet.
Каждый аргумент в annotate() — это аннотация, которая будет добавлена к каждому объекту в QuerySet в результате.
Функции агрегации, предоставляемые Django, описаны в Функциях агрегации ниже.
Аннотации, заданные с использованием именованных аргументов, будут использовать имя аргумента в качестве псевдонима аннотации. Для анонимных аргументов будет сгенерирован псевдоним на основе имени агрегационной функции и поля модели, которое агрегируется. Только агрегационные выражения, ссылающиеся на одно поле, могут быть анонимными аргументами. Всё остальное должно быть именованным аргументом.
Например, если вы работаете со списком блогов, вы можете захотеть определить, сколько записей было сделано в каждом блоге:
>>> from django.db.models import Count
>>> q = Blog.objects.annotate(Count("entry"))
# The name of the first blog
>>> q[0].name
'Blogasaurus'
# The number of entries on the first blog
>>> q[0].entry__count
42
Модель Blog сама по себе не определяет атрибут entry__count, но с помощью именованного аргумента для указания агрегационной функции вы можете контролировать имя аннотации:
>>> q = Blog.objects.annotate(number_of_entries=Count("entry"))
# The number of entries on the first blog, using the name provided
>>> q[0].number_of_entries
42
Подробное обсуждение агрегации см. в руководстве по теме Агрегации.
alias()
-
alias(*args, **kwargs)
То же самое, что и annotate(), но вместо аннотации объектов в QuerySet, сохраняет выражение для последующего использования с другими методами QuerySet. Это полезно, когда результат выражения сам по себе не нужен, но он используется для фильтрации, сортировки или как часть сложного выражения. Невыбор неиспользуемого значения удаляет излишнюю работу из базы данных, что должно привести к лучшей производительности.
Например, если вы хотите найти блоги с более чем 5 записями, но вас не интересует точное количество записей, вы можете сделать это так:
>>> from django.db.models import Count
>>> blogs = Blog.objects.alias(entries=Count("entry")).filter(entries__gt=5)
alias() можно использовать в сочетании с annotate(), exclude(), filter(), order_by() и update(). Чтобы использовать алиасированное выражение с другими методами (например, aggregate()), вы должны преобразовать его в аннотацию:
Blog.objects.alias(entries=Count("entry")).annotate(
entries=F("entries"),
).aggregate(Sum("entries"))
filter() и order_by() могут принимать выражения напрямую, но построение и использование выражений часто происходит не в одном месте (например, метод QuerySet создаёт выражения для последующего использования в представлении). alias() позволяет строить сложные выражения по частям, возможно, охватывая несколько методов и модулей, ссылаясь на части выражения по их псевдонимам и используя только annotate() для конечного результата.
order_by()
-
order_by(*fields)
По умолчанию результаты, возвращаемые QuerySet, упорядочиваются в соответствии с кортежем сортировки, заданным в опции ordering в модели Meta. Вы можете переопределить это для каждой отдельной QuerySet с помощью метода order_by.
Пример:
Entry.objects.filter(pub_date__year=2005).order_by("-pub_date", "headline")
Результат выше будет отсортирован по pub_date в порядке убывания, затем по headline в порядке возрастания. Минус перед "-pub_date" указывает порядок убывания. Порядок возрастания подразумевается. Чтобы отсортировать случайным образом, используйте "?", как показано ниже:
Entry.objects.order_by("?")
Примечание: запросы с order_by('?') могут быть дорогими и медленными, в зависимости от используемого вами баз данных.
Чтобы отсортировать по полю в другой модели, используйте ту же синтаксическую конструкцию, что и при запросах через связи между моделями. То есть имя поля, за которым следует двойное подчеркивание (__), за которым следует имя поля в новой модели, и так далее для всех необходимых объединений моделей. Например:
Entry.objects.order_by("blog__name", "headline")
Если вы попытаетесь отсортировать по полю, которое является связью с другой моделью, Django будет использовать стандартную сортировку связанной модели или сортировать по первичному ключу связанной модели, если не указано Meta.ordering. Например, поскольку в модели Blog не указана стандартная сортировка:
Entry.objects.order_by("blog")
…тождественно:
Entry.objects.order_by("blog__id")
Если у Blog была ordering = ['name'], тогда первый запрос был бы идентичен:
Entry.objects.order_by("blog__name")
Вы также можете отсортировать по выражениям запроса, вызвав asc() или desc() для выражения:
Entry.objects.order_by(Coalesce("summary", "headline").desc())
asc() и desc() имеют аргументы (nulls_first и nulls_last), которые управляют сортировкой нулевых значений.
Будьте осторожны при сортировке по полям связанных моделей, если вы также используете 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(), чтобы изменить порядок возвращения элементов в наборе запросов. Вызов reverse() второй раз восстановит порядок в исходном направлении.
Чтобы получить «последние» пять элементов в наборе запросов, можно сделать так:
my_queryset.reverse()[:5]
Обратите внимание, что это не совсем то же самое, что срезы с конца последовательности в Python. Приведённый выше пример вернёт последний элемент сначала, затем предпоследний и так далее. Если бы у нас была Python-последовательность и мы посмотрели на seq[-5:], мы бы увидели пятый с конца элемент сначала. Django не поддерживает такой режим доступа (срезы с конца), потому что сделать это эффективно в SQL невозможно.
Также обратите внимание, что reverse() обычно следует вызывать только для набора запросов QuerySet, у которого определён порядок сортировки (например, при запросе к модели, которая определяет порядок по умолчанию, или при использовании order_by()). Если такой порядок сортировки для данного набора запросов QuerySet не определён, вызов reverse() на нём не окажет существенного влияния (порядок был неопределён до вызова reverse(), и останется неопределённым после).
distinct()
-
distinct(*fields)
Возвращает новый набор запросов QuerySet, который использует SELECT DISTINCT в своём SQL-запросе. Это устраняет дубликаты строк из результатов запроса.
По умолчанию набор запросов QuerySet не устраняет дубликаты строк. На практике это редко вызывает проблемы, так как простые запросы, такие как Blog.objects.all() , не вводят возможность получения дубликатов строк результатов. Однако, если ваш запрос охватывает несколько таблиц, возможно получение дубликатов результатов при оценке набора запросов QuerySet. Именно тогда вам понадобится distinct().
Примечание
Любые поля, используемые в вызове order_by(), включаются в столбцы SQL SELECT. Это иногда приводит к неожиданным результатам, когда используется совместно с distinct(). Если вы сортируете по полям из связанной модели, эти поля будут добавлены к выбранным столбцам, и это может заставить строки, которые в противном случае были бы дубликатами, отображаться как разные. Поскольку дополнительные столбцы не отображаются в результатах (они нужны только для поддержки сортировки), иногда кажется, что возвращаются неотсортированные результаты.
Аналогично, если вы используете запрос values() для ограничения выбранных столбцов, столбцы, используемые в любом вызове order_by() (или порядке сортировки модели по умолчанию), всё ещё будут задействованы и могут повлиять на уникальность результатов.
Вывод заключается в том, что при использовании distinct() будьте осторожны при сортировке по связанным моделям. Аналогично, при использовании distinct() и values() вместе, будьте осторожны при сортировке по полям, не указанным в вызове values().
Только для PostgreSQL вы можете передать позиционные аргументы (*fields) для указания имён полей, к которым должно применяться DISTINCT. Это переводится в SQL-запрос SELECT DISTINCT ON. Вот в чём разница. При обычном вызове distinct(), база данных сравнивает каждое поле в каждой строке при определении, какие строки являются уникальными. При вызове distinct() с указанием имён полей база данных будет сравнивать только указанные имена полей.
Примечание
Когда вы указываете имена полей, вы обязательно должны указать порядок в 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)
Возвращает QuerySet, который вычисляется как список объектов datetime.datetime, представляющих все доступные даты определенного типа в содержании QuerySet.
field_name должно быть именем DateTimeField вашей модели.
kind должно быть "year", "month", "week", "day", "hour", "minute", или "second". Каждый объект datetime.datetime в списке результатов «обрезается» до указанного type.
order, по умолчанию 'ASC', должно быть 'ASC' или 'DESC'. Это определяет, как упорядочить результаты.
tzinfo определяет часовой пояс, в который преобразуются даты и время перед обрезкой. Действительно, у заданной даты и времени есть разные представления в зависимости от используемого часового пояса. Этот параметр должен быть объектом datetime.tzinfo. Если он None, Django использует текущий часовой пояс. Он не оказывает влияния, когда USE_TZ равно False.
Примечание
Эта функция выполняет преобразования часовых поясов непосредственно в базе данных. Вследствие этого ваша база данных должна уметь интерпретировать значение tzinfo.tzname(None). Это приводит к следующим требованиям:
- SQLite: никаких требований. Преобразования выполняются в Python.
- PostgreSQL: никаких требований (см. Часовые пояса).
- Oracle: никаких требований (см. Выбор файла часового пояса).
- MySQL: загрузите таблицы часовых поясов с помощью mysql_tzinfo_to_sql.
none()
-
none()
Вызов none() создаст запрос, который никогда не возвращает объекты, и ни один запрос не будет выполнен при обращении к результатам. Запрос qs.none() является экземпляром EmptyQuerySet.
Примеры:
>>> Entry.objects.none() <QuerySet []> >>> from django.db.models.query import EmptyQuerySet >>> isinstance(Entry.objects.none(), EmptyQuerySet) True
all()
-
all()
Возвращает копию текущего QuerySet (или подкласса QuerySet). Это может быть полезно в ситуациях, когда вы можете передать менеджер модели или QuerySet и выполнить дальнейшее фильтрацию результата. После вызова all() для любого из объектов у вас точно будет QuerySet для работы.
Когда QuerySet вычисляется, он обычно кэширует результаты. Если данные в базе данных могли измениться с момента вычисления QuerySet, вы можете получить обновлённые результаты для того же запроса, вызвав all() для ранее вычисленного QuerySet.
union()
-
union(*other_qs, all=False)
Использует оператор SQL UNION для объединения результатов двух или более QuerySet. Например:
>>> qs1.union(qs2, qs3)
Оператор UNION по умолчанию выбирает только уникальные значения. Чтобы разрешить дубликаты, используйте аргумент all=True.
union(), intersection(), и difference() возвращают экземпляры модели типа первой QuerySet даже если аргументы являются QuerySet других моделей. Передача разных моделей работает, если список SELECT одинаков во всех QuerySet (по крайней мере типы, имена не важны, пока типы в том же порядке). В таких случаях вы должны использовать имена столбцов из первой QuerySet в QuerySet методах, применяемых к результатам QuerySet. Например:
>>> qs1 = Author.objects.values_list("name")
>>> qs2 = Entry.objects.values_list("headline")
>>> qs1.union(qs2).order_by("name")
Кроме того, разрешены только LIMIT, OFFSET, COUNT(*), ORDER BY, и указание столбцов (т.е. срезы, count(), exists(), order_by() и values()/values_list()) для полученного QuerySet. Кроме того, базы данных накладывают ограничения на разрешенные операции в комбинированных запросах. Например, большинство баз данных не допускают LIMIT или OFFSET в комбинированных запросах.
intersection()
-
intersection(*other_qs)
Использует оператор SQL INTERSECT для возврата общих элементов двух или более QuerySet. Например:
>>> qs1.intersection(qs2, qs3)
См. union() для некоторых ограничений.
difference()
-
difference(*other_qs)
Использует оператор SQL EXCEPT для сохранения только элементов, присутствующих в QuerySet, но не в некоторых других QuerySet. Например:
>>> qs1.difference(qs2, qs3)
См. union() для некоторых ограничений.
select_related()
Возвращает QuerySet, который будет «следовать» за связями внешних ключей, выбирая дополнительные данные связанных объектов при выполнении запроса. Это повышает производительность, приводя к одному более сложному запросу, но означает, что последующее использование связей внешних ключей не потребует запросов к базе данных.
Следующие примеры иллюстрируют разницу между обычными и select_related() поиском. Вот стандартный поиск:
# Hits the database. e = Entry.objects.get(id=5) # Hits the database again to get the related Blog object. b = e.blog
А вот select_related поиск:
# Hits the database.
e = Entry.objects.select_related("blog").get(id=5)
# Doesn't hit the database, because e.blog has been prepopulated
# in the previous query.
b = e.blog
Вы можете использовать select_related() с любым набором объектов запроса:
from django.utils import timezone
# Find all the blogs with entries scheduled to be published in the future.
blogs = set()
for e in Entry.objects.filter(pub_date__gt=timezone.now()).select_related("blog"):
# Without select_related(), this would make a database query for each
# loop iteration in order to fetch the related blog for each entry.
blogs.add(e.blog)
Порядок цепочки вызовов filter() и select_related() не имеет значения. Эти наборы запросов эквивалентны:
Entry.objects.filter(pub_date__gt=timezone.now()).select_related("blog")
Entry.objects.select_related("blog").filter(pub_date__gt=timezone.now())
Вы можете аналогично получать внешние ключи. Если у вас есть следующие модели:
from django.db import models
class City(models.Model):
# ...
pass
class Person(models.Model):
# ...
hometown = models.ForeignKey(
City,
on_delete=models.SET_NULL,
blank=True,
null=True,
)
class Book(models.Model):
# ...
author = models.ForeignKey(Person, on_delete=models.CASCADE)
… тогда вызов Book.objects.select_related('author__hometown').get(id=4) кэширует связанные Person и связанные City:
# Hits the database with joins to the author and hometown tables.
b = Book.objects.select_related("author__hometown").get(id=4)
p = b.author # Doesn't hit the database.
c = p.hometown # Doesn't hit the database.
# Without select_related()...
b = Book.objects.get(id=4) # Hits the database.
p = b.author # Hits the database.
c = p.hometown # Hits the database.
Вы можете обратиться к любой связи ForeignKey или OneToOneField в списке полей, переданных в select_related().
Вы также можете обратиться к обратной связи OneToOneField в списке полей, переданных в select_related — то есть, вы можете пройти по связи OneToOneField обратно к объекту, в котором определено поле. Вместо указания имени поля используйте related_name для поля в связанном объекте.
Возможны ситуации, когда вы хотите вызвать select_related() со многими связанными объектами или когда вам не известны все связи. В этих случаях можно вызвать select_related() без аргументов. Это позволит проследить все непустые внешние ключи, которые он может найти — пустые внешние ключи должны быть указаны. В большинстве случаев это не рекомендуется, так как, вероятно, это сделает базовый запрос более сложным и вернёт больше данных, чем это необходимо.
Если вам нужно очистить список связанных полей, добавленных предыдущими вызовами select_related для QuerySet, вы можете передать None в качестве параметра:
>>> without_relations = queryset.select_related(None)
Цепочка вызовов select_related работает аналогично другим методам — то есть select_related('foo', 'bar') эквивалентно select_related('foo').select_related('bar').
prefetch_related()
Возвращает QuerySet , который автоматически получит в одной группе связанные объекты для каждого из указанных поисков.
Это имеет сходное назначение с select_related, так как оба предназначены для прекращения потока запросов к базе данных, возникающего при доступе к связанным объектам, но стратегия сильно отличается.
select_related работает путём создания SQL-соединения и включения полей связанного объекта в SELECT оператор. По этой причине select_related получает связанные объекты в одном запросе к базе данных. Однако, чтобы избежать намного большего набора результатов, который получился бы в результате объединения по связи «многие», select_related ограничен однозначными связями — внешним ключом и один-к-одному.
prefetch_related, с другой стороны, выполняет отдельный поиск для каждой связи и выполняет «объединение» в Python. Это позволяет ей предварительно загружать многие-ко-многим, многие-к-одному и GenericRelation объекты, чего нельзя сделать с помощью select_related, в дополнение к внешним ключам и связям один-к-одному, которые поддерживаются select_related. Также поддерживает предварительную загрузку GenericForeignKey, но набор запросов для каждого ContentType должен быть предоставлен в параметре querysets GenericPrefetch.
Добавлена поддержка предварительной загрузки GenericForeignKey с не однородным набором результатов.
Например, предположим, что у вас есть эти модели:
from django.db import models
class Topping(models.Model):
name = models.CharField(max_length=30)
class Pizza(models.Model):
name = models.CharField(max_length=50)
toppings = models.ManyToManyField(Topping)
def __str__(self):
return "%s (%s)" % (
self.name,
", ".join(topping.name for topping in self.toppings.all()),
)
и выполняете:
>>> Pizza.objects.all() ["Hawaiian (ham, pineapple)", "Seafood (prawns, smoked salmon)"...
Проблема в том, что каждый раз, когда Pizza.__str__() запрашивает self.toppings.all(), ему необходимо выполнить запрос к базе данных, поэтому Pizza.objects.all() будет выполнять запрос к таблице Toppings для каждого элемента в пицце QuerySet.
Мы можем уменьшить до двух запросов с помощью prefetch_related:
>>> Pizza.objects.prefetch_related("toppings")
Это подразумевает self.toppings.all() для каждого Pizza; теперь каждый раз, когда self.toppings.all() вызывается, вместо того, чтобы обращаться к базе данных за элементами, он найдёт их в кэше предварительной загрузки QuerySet, который был заполнен в одном запросе.
То есть, все соответствующие дополнения будут получены в одном запросе и использованы для создания QuerySets , которые имеют предварительно заполненный кэш соответствующих результатов; эти QuerySets затем используются в вызовах self.toppings.all().
Дополнительные запросы в prefetch_related() выполняются после того, как QuerySet начинает оцениваться, и основной запрос был выполнен.
Обратите внимание, что нет механизма для предотвращения изменения данных в базе данных другим запросом между выполнением основного запроса и дополнительных запросов, что может привести к несогласованным результатам. Например, если Pizza удаляется после выполнения основного запроса, его дополнения не будут возвращены в дополнительном запросе, и будет казаться, что у пиццы нет дополнений:
>>> Pizza.objects.prefetch_related("toppings")
# "Hawaiian" Pizza was deleted in another shell.
<QuerySet [<Pizza: Hawaiian ()>, <Pizza: Seafood (prawns, smoked salmon)>]>
Если у вас есть итерируемый список экземпляров моделей, вы можете предварительно загрузить связанные атрибуты для этих экземпляров с помощью функции prefetch_related_objects().
Обратите внимание, что кэш результатов основного QuerySet и всех указанных связанных объектов затем будет полностью загружен в память. Это изменяет типичное поведение QuerySets, которые обычно пытаются избежать загрузки всех объектов в память до их необходимости, даже после выполнения запроса в базе данных.
Примечание
Помните, что, как всегда при использовании QuerySets, любые последующие цепные методы, подразумевающие другой запрос к базе данных, проигнорируют ранее кэшированные результаты и извлекут данные с помощью нового запроса к базе данных. Таким образом, если вы напишете следующее:
>>> pizzas = Pizza.objects.prefetch_related("toppings")
>>> [list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]
…то тот факт, что pizza.toppings.all() был предварительно загружен, вам не поможет. prefetch_related('toppings') подразумевает pizza.toppings.all(), но pizza.toppings.filter() — это новый и другой запрос. Кэшированный предварительно загруженный результат здесь не поможет; на самом деле это ухудшает производительность, так как вы выполнили запрос к базе данных, который не использовали. Поэтому используйте эту функцию с осторожностью!
Кроме того, если вы вызываете методы изменения данных в базе данных add(), create(), remove(), clear() или set() в related managers, любой кэшированный предварительно загруженный результат для связи будет очищен.
Вы также можете использовать обычный синтаксис объединения для работы с взаимосвязанными полями. Предположим, что у нас есть дополнительная модель в приведенном выше примере:
class Restaurant(models.Model):
pizzas = models.ManyToManyField(Pizza, related_name="restaurants")
best_pizza = models.ForeignKey(
Pizza, related_name="championed_by", on_delete=models.CASCADE
)
Следующие варианты допустимы:
>>> Restaurant.objects.prefetch_related("pizzas__toppings")
Это предварительно загрузит все пиццы, принадлежащие ресторанам, и все начинки, принадлежащие этим пиццам. Это приведет к общему количеству 3 запросов к базе данных — один для ресторанов, один для пицц и один для начинок.
>>> Restaurant.objects.prefetch_related("best_pizza__toppings")
Это получит лучшую пиццу и все начинки для лучшей пиццы для каждого ресторана. Это будет выполнено в 3 запросах к базе данных — один для ресторанов, один для «лучших пицц» и один для начинок.
Связь best_pizza также можно получить с помощью select_related, чтобы уменьшить количество запросов до 2:
>>> Restaurant.objects.select_related("best_pizza").prefetch_related("best_pizza__toppings")
Поскольку предварительная загрузка выполняется после основного запроса (который включает объединения, необходимые для select_related), он может определить, что объекты best_pizza уже загружены, и пропустит повторную загрузку.
Цепные вызовы prefetch_related будут накапливать предварительно загруженные запросы. Чтобы очистить любое поведение prefetch_related, передайте None в качестве параметра:
>>> non_prefetched = qs.prefetch_related(None)
Одно различие, которое следует учитывать при использовании prefetch_related, заключается в том, что объекты, созданные запросом, могут быть общими между различными объектами, к которым они относятся, т. е. один экземпляр Python-модели может появляться в нескольких точках дерева возвращаемых объектов. Обычно это поведение не представляет проблемы и, фактически, экономит память и время процессора.
Хотя prefetch_related поддерживает предварительную загрузку GenericForeignKey взаимосвязей, количество запросов будет зависеть от данных. Поскольку GenericForeignKey может ссылаться на данные в нескольких таблицах, требуется по одному запросу на каждую таблицу, на которую ссылаются, а не один запрос ко всем элементам. Могут быть дополнительные запросы к таблице ContentType, если соответствующие строки еще не были загружены.
prefetch_related в большинстве случаев будет реализован с помощью SQL-запроса, использующего оператор «IN». Это означает, что для большого QuerySet может быть сгенерирована большая строка «IN», которая в зависимости от базы данных может иметь собственные проблемы с производительностью при обработке или выполнении SQL-запроса. Всегда производите профилирование для вашего сценария использования!
Если вы используете iterator() для запуска запроса, вызовы prefetch_related() будут наблюдаться только в том случае, если предоставлено значение для chunk_size.
Вы можете использовать объект Prefetch для дальнейшего управления операцией предварительной загрузки.
В простейшем виде Prefetch эквивалентен традиционным запросам на основе строк:
>>> from django.db.models import Prefetch
>>> Restaurant.objects.prefetch_related(Prefetch("pizzas__toppings"))
Вы можете предоставить пользовательский набор с необязательным аргументом queryset. Это можно использовать для изменения стандартного порядка сортировки набора запросов:
>>> Restaurant.objects.prefetch_related(
... Prefetch("pizzas__toppings", queryset=Toppings.objects.order_by("name"))
... )
Или вызвать select_related(), где это возможно, чтобы еще больше уменьшить количество запросов:
>>> Pizza.objects.prefetch_related(
... Prefetch("restaurants", queryset=Restaurant.objects.select_related("best_pizza"))
... )
Вы также можете назначить предварительно загруженный результат пользовательскому атрибуту с необязательным аргументом to_attr. Результат будет сохранен непосредственно в списке.
Это позволяет выполнять предварительную загрузку одной и той же связи несколько раз с разными QuerySet; например:
>>> vegetarian_pizzas = Pizza.objects.filter(vegetarian=True)
>>> Restaurant.objects.prefetch_related(
... Prefetch("pizzas", to_attr="menu"),
... Prefetch("pizzas", queryset=vegetarian_pizzas, to_attr="vegetarian_menu"),
... )
Запросы, созданные с пользовательскими to_attr, по-прежнему можно обходить обычным способом с помощью других запросов:
>>> vegetarian_pizzas = Pizza.objects.filter(vegetarian=True)
>>> Restaurant.objects.prefetch_related(
... Prefetch("pizzas", queryset=vegetarian_pizzas, to_attr="vegetarian_menu"),
... "vegetarian_menu__toppings",
... )
Использование to_attr рекомендуется при фильтрации результатов предварительной загрузки, поскольку это менее неоднозначно, чем хранение отфильтрованного результата в кэше связанного менеджера:
>>> queryset = Pizza.objects.filter(vegetarian=True)
>>>
>>> # Recommended:
>>> restaurants = Restaurant.objects.prefetch_related(
... Prefetch("pizzas", queryset=queryset, to_attr="vegetarian_pizzas")
... )
>>> vegetarian_pizzas = restaurants[0].vegetarian_pizzas
>>>
>>> # Not recommended:
>>> restaurants = Restaurant.objects.prefetch_related(
... Prefetch("pizzas", queryset=queryset),
... )
>>> vegetarian_pizzas = restaurants[0].pizzas.all()
Пользовательская предварительная загрузка также работает с одиночными связанными отношениями, такими как прямая ForeignKey или OneToOneField. Обычно для этих отношений следует использовать select_related(), но есть ряд случаев, когда предварительная загрузка с пользовательским QuerySet полезна:
- Вы хотите использовать
QuerySet, который выполняет дальнейшую предварительную загрузку связанных моделей. - Вы хотите предварительно загрузить только подмножество связанных объектов.
-
Вы хотите использовать методы оптимизации производительности, такие как
deferred fields:>>> queryset = Pizza.objects.only("name") >>> >>> restaurants = Restaurant.objects.prefetch_related( ... Prefetch("best_pizza", queryset=queryset) ... )
При использовании нескольких баз данных Prefetch будет учитывать ваш выбор базы данных. Если внутренний запрос не указывает базу данных, он будет использовать базу данных, выбранную внешним запросом. Все следующие варианты допустимы:
>>> # Both inner and outer queries will use the 'replica' database
>>> Restaurant.objects.prefetch_related("pizzas__toppings").using("replica")
>>> Restaurant.objects.prefetch_related(
... Prefetch("pizzas__toppings"),
... ).using("replica")
>>>
>>> # Inner will use the 'replica' database; outer will use 'default' database
>>> Restaurant.objects.prefetch_related(
... Prefetch("pizzas__toppings", queryset=Toppings.objects.using("replica")),
... )
>>>
>>> # Inner will use 'replica' database; outer will use 'cold-storage' database
>>> Restaurant.objects.prefetch_related(
... Prefetch("pizzas__toppings", queryset=Toppings.objects.using("replica")),
... ).using("cold-storage")
Примечание
Порядок запросов имеет значение.
Рассмотрим следующие примеры:
>>> prefetch_related("pizzas__toppings", "pizzas")
Это работает даже без упорядочивания, так как 'pizzas__toppings' уже содержит всю необходимую информацию, поэтому второй аргумент 'pizzas' фактически избыточен.
>>> prefetch_related("pizzas__toppings", Prefetch("pizzas", queryset=Pizza.objects.all()))
Это вызовет ValueError, из-за попытки переопределить набор запросов ранее увиденного запроса. Обратите внимание, что неявный набор запросов был создан для обхода 'pizzas' в рамках запроса 'pizzas__toppings'.
>>> prefetch_related("pizza_list__toppings", Prefetch("pizzas", to_attr="pizza_list"))
Это вызовет AttributeError, потому что 'pizza_list' еще не существует, когда обрабатывается 'pizza_list__toppings'.
Это соображение не ограничивается использованием Prefetch объектов. Некоторые расширенные техники могут потребовать выполнения запросов в определенном порядке, чтобы избежать создания дополнительных запросов; поэтому рекомендуется всегда внимательно упорядочивать аргументы prefetch_related.
extra()
-
extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
Иногда синтаксис запросов Django сам по себе не может легко выразить сложное WHERE условие. В таких крайних случаях Django предоставляет extra() QuerySet модификатор — возможность встраивания специфических условий в SQL, генерируемый QuerySet.
Используйте этот метод в крайнем случае
Это старая API, которую мы планируем в какой-то момент в будущем упразднить. Используйте её только если вы не можете выразить свой запрос с помощью других методов набора запросов. Если вам всё-таки нужно её использовать, пожалуйста, отправьте запрос с помощью ключа QuerySet.extra со своим случаем использования (сначала, пожалуйста, проверьте список существующих запросов), чтобы мы могли улучшить API QuerySet, позволяя удалить extra(). Мы больше не улучшаем и не исправляем ошибки для этого метода.
Например, такое использование extra():
>>> qs.extra(
... select={"val": "select col from sometable where othercol = %s"},
... select_params=(someparam,),
... )
эквивалентно:
>>> qs.annotate(val=RawSQL("select col from sometable where othercol = %s", (someparam,)))
Основное преимущество использования RawSQL заключается в том, что вы можете установить output_field при необходимости. Основной недостаток заключается в том, что если вы ссылаетесь на какой-либо псевдоним таблицы набора запросов в сыром SQL, то Django может изменить этот псевдоним (например, когда набор запросов используется в качестве подзапроса в другом запросе).
Предупреждение
Вы должны быть очень осторожны при использовании extra(). Каждый раз, когда вы используете его, вы должны экранировать любые параметры, которые может контролировать пользователь, используя params для защиты от атак SQL-инъекций.
Также не следует заключать в кавычки заполнитель в строке SQL. Этот пример уязвим для атак SQL-инъекций из-за кавычек вокруг %s:
SELECT col FROM sometable WHERE othercol = '%s' # unsafe!
Вы можете узнать больше о том, как работает защита от атак SQL-инъекций Django защита от атак SQL-инъекций.
По определению, эти дополнительные запросы могут быть непереносимы в разные СУБД (поскольку вы явно пишете SQL-код) и нарушают принцип DRY, поэтому следует избегать их, если это возможно.
Укажите один или несколько из params, select, where или tables. Ни один из аргументов не является обязательным, но вы должны использовать по крайней мере один из них.
-
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. Вы можете вручную добавлять таблицы в условие SQLFROMс помощью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()) с аргументом из агрегации (например, используя результат annotate()) не имеет смысла: это вызовет исключение. Агрегированные значения всегда будут загружены в результирующий набор результатов запроса.
Примечание
Методы defer() (и его аналог only()) предназначены только для продвинутых случаев. Они обеспечивают оптимизацию в тех случаях, когда вы тщательно проанализировали свои запросы и точно знаете, какая информация вам нужна, и измерили, что различие между возвращением необходимых полей и полного набора полей модели будет значительным.
Даже если вы считаете, что находитесь в ситуации для продвинутого использования, используйте defer() только тогда, когда вы не можете определить на этапе загрузки набора результатов запроса, нужны ли вам дополнительные поля или нет. Если вы часто загружаете и используете определённый подмножество данных, лучше всего нормализовать ваши модели и поместить не загруженные данные в отдельную модель (и таблицу базы данных). Если столбцы должны оставаться в одной таблице по какой-то причине, создайте модель с Meta.managed = False (см. документацию managed attribute) , содержащую только поля, которые вы обычно загружаете и используете, в тех местах, где вы бы иначе вызвали defer(). Это делает ваш код более понятным для читателя, немного быстрее и потребляет немного меньше памяти в процессе Python.
Например, обе эти модели используют одну и ту же базу данных:
class CommonlyUsedModel(models.Model):
f1 = models.CharField(max_length=10)
class Meta:
managed = False
db_table = "app_largetable"
class ManagedModel(models.Model):
f1 = models.CharField(max_length=10)
f2 = models.CharField(max_length=10)
class Meta:
db_table = "app_largetable"
# Two equivalent QuerySets:
CommonlyUsedModel.objects.all()
ManagedModel.objects.defer("f2")
Если многим полям требуется дублирование в необработанной модели, лучше всего создать абстрактную модель с общими полями, а затем наследоваться от абстрактной модели для необработанной и обработанной моделей.
Примечание
При вызове save() для экземпляров с отложенными полями, будут сохранены только загруженные поля. См. save() для получения дополнительной информации.
only()
-
only(*fields)
Метод only() по сути является противоположностью defer(). Только поля, переданные в этот метод и которые не уже указаны как отложенные, загружаются немедленно при вычислении набора запросов.
Если у вас есть модель, где почти все поля должны быть отложены, использование only() для указания дополнительного набора полей может привести к более простому коду.
Предположим, у вас есть модель с полями name, age и biography. Два следующих набора запросов идентичны по отложенным полям:
Person.objects.defer("age", "biography")
Person.objects.only("name")
Всякий раз, когда вы вызываете only(), он заменяет набор полей, которые загружаются немедленно. Название метода является мнемоническим: только эти поля загружаются немедленно; остальные откладываются. Таким образом, последовательные вызовы к only() приводят к тому, что в рассмотрение принимаются только последние поля:
# This will defer all fields except the headline.
Entry.objects.only("body", "rating").only("headline")
Так как defer() действует поэтапно (добавляя поля в список отложенных), вы можете объединить вызовы only() и defer(), и поведение будет логичным:
# Final result is that everything except "headline" is deferred.
Entry.objects.only("headline", "body").defer("body")
# Final result loads headline immediately.
Entry.objects.defer("body").only("headline", "body")
Все предостережения в примечании к документации defer() также применяются к only(). Используйте его осторожно и только после исчерпания других вариантов.
Использование only() и опущение поля, запрошенного с помощью select_related() — тоже ошибка. С другой стороны, вызов only() без аргументов вернёт все поля (включая аннотации), полученные набором запросов.
Как и в случае с defer(), вы не можете получить доступ к не загруженным полям из асинхронного кода и ожидать их загрузки. Вместо этого вы получите исключение SynchronousOnlyOperation. Убедитесь, что все поля, к которым вы можете получить доступ, включены в ваш вызов only().
Примечание
При вызове save() для экземпляров с отложенными полями, будут сохранены только загруженные поля. Более подробную информацию см. в save().
Примечание
При использовании defer() после only(), поля в defer() переопределят only() для полей, которые указаны в обоих.
using()
-
using(alias)
Этот метод используется для управления базой данных, относительно которой будет вычисляться набор запросов, если вы используете более одной базы данных. Единственным аргументом этого метода является псевдоним базы данных, как определено в DATABASES.
Например:
# queries the database with the 'default' alias.
>>> Entry.objects.all()
# queries the database with the 'backup' alias
>>> Entry.objects.using("backup")
select_for_update()
-
select_for_update(nowait=False, skip_locked=False, of=(), no_key=False)
Возвращает набор запросов, который будет блокировать строки до конца транзакции, генерируя оператор SELECT ... FOR UPDATE SQL на поддерживаемых базах данных.
Например:
from django.db import transaction
entries = Entry.objects.select_for_update().filter(author=request.user)
with transaction.atomic():
for entry in entries:
...
Когда набор запросов вычисляется (в данном случае for entry in entries ), все сопоставленные записи будут заблокированы до конца блока транзакции, что означает, что другие транзакции будут предотвращены от изменения или получения блокировок на них.
Обычно, если другая транзакция уже получила блокировку на одной из выбранных строк, запрос будет блокироваться, пока блокировка не будет освобождена. Если это не нужно, вызовите select_for_update(nowait=True). Это сделает вызов неблокирующим. Если блокировка конфликтует с другой транзакцией, будет поднято исключение DatabaseError, когда набор запросов будет вычисляться. Вы также можете пропустить заблокированные строки, используя select_for_update(skip_locked=True) вместо этого. nowait и skip_locked взаимно исключают друг друга, и попытка вызова select_for_update() с обоими параметрами включенными приведёт к исключению ValueError.
По умолчанию select_for_update() блокирует все строки, которые выбираются запросом. Например, строки связанных объектов, указанных в select_related(), блокируются в дополнение к строкам модели набора запросов. Если это не нужно, укажите связанные объекты, которые вы хотите заблокировать в select_for_update(of=(...)) , используя тот же синтаксис полей, что и select_related(). Используйте значение 'self' для ссылки на модель набора запросов.
Блокировка родительских моделей в select_for_update(of=(...))
Если вы хотите заблокировать родительские модели при использовании наследования по нескольким таблицам, вы должны указать поля ссылки на родителя (по умолчанию <parent_model_name>_ptr) в аргументе of. Например:
Restaurant.objects.select_for_update(of=("self", "place_ptr"))
Использование select_for_update(of=(...)) со специфицированными полями
Если вы хотите заблокировать модели и указать выбранные поля, например, с помощью values(), вы должны выбрать по крайней мере одно поле из каждой модели в аргументе of . Модели без выбранных полей не будут заблокированы.
Только для PostgreSQL, вы можете передать no_key=True для получения слабой блокировки, которая все еще позволяет создавать строки, которые просто ссылаются на заблокированные строки (например, через внешний ключ), в то время как блокировка активна. Документация PostgreSQL содержит больше информации об режимах блокировки на уровне строк.
Вы не можете использовать select_for_update() для незначащих связей:
>>> Person.objects.select_related("hometown").select_for_update()
Traceback (most recent call last):
...
django.db.utils.NotSupportedError: FOR UPDATE cannot be applied to the nullable side of an outer join
Чтобы избежать этого ограничения, вы можете исключить нулевые объекты, если вам они не нужны:
>>> Person.objects.select_related("hometown").select_for_update().exclude(hometown=None)
<QuerySet [<Person: ...)>, ...]>
postgresql, oracle, и mysql бэкенды баз данных поддерживают select_for_update(). Однако MariaDB поддерживает только аргумент nowait, MariaDB 10.6+ также поддерживает аргумент skip_locked, а MySQL поддерживает аргументы nowait, skip_locked, и of. Аргумент no_key поддерживается только в PostgreSQL.
Передача nowait=True, skip_locked=True, no_key=True, или of в select_for_update() с помощью бэкэндов баз данных, которые не поддерживают эти параметры, таких как MySQL, вызывает NotSupportedError. Это предотвращает неожиданную блокировку кода.
Вычисление набора запросов с select_for_update() в режиме автокоммита в бэкендах, которые поддерживают SELECT ... FOR UPDATE, является ошибкой TransactionManagementError, потому что строки в этом случае не блокируются. Если это было бы разрешено, это способствовало бы повреждению данных и могло быть легко вызвано кодом, который ожидает выполнения вне транзакции.
Использование select_for_update() в бэкендах, которые не поддерживают SELECT ... FOR UPDATE (таких как SQLite), не оказывает никакого влияния. SELECT ... FOR UPDATE не будет добавлен в запрос, и ошибка не возникает, если select_for_update() используется в режиме автокоммита.
Предупреждение
Хотя select_for_update() обычно не срабатывает в режиме автокоммита, так как TestCase автоматически оборачивает каждый тест в транзакцию, вызов select_for_update() в TestCase даже вне блока atomic() пройдёт (возможно, неожиданно) без поднятия TransactionManagementError. Для правильного тестирования select_for_update() вы должны использовать TransactionTestCase.
Некоторые выражения могут быть не поддерживаемы
PostgreSQL не поддерживает select_for_update() с выражениями Window.
raw()
-
raw(raw_query, params=(), translations=None, using=None)
Принимает сырой SQL-запрос, выполняет его и возвращает экземпляр django.db.models.query.RawQuerySet. Этот экземпляр RawQuerySet можно перебирать так же, как обычный набор запросов QuerySet, чтобы получить экземпляры объектов.
См. Выполнение сырых SQL-запросов для получения дополнительной информации.
Предупреждение
raw() всегда запускает новый запрос и не учитывает предыдущую фильтрацию. Поэтому его обычно следует вызывать из Manager или из нового экземпляра QuerySet.
Операторы, возвращающие новые наборы запросов
Объединенные наборы запросов должны использовать одну и ту же модель.
И (&)
Объединяет два набора запросов QuerySet с помощью оператора SQL AND аналогично цепочке фильтров.
Следующее эквивалентно:
Model.objects.filter(x=1) & Model.objects.filter(y=2) Model.objects.filter(x=1).filter(y=2)
SQL эквивалент:
SELECT ... WHERE x=1 AND y=2
ИЛИ (|)
Объединяет два QuerySet с помощью оператора SQL OR.
Следующее эквивалентно:
Model.objects.filter(x=1) | Model.objects.filter(y=2) from django.db.models import Q Model.objects.filter(Q(x=1) | Q(y=2))
SQL эквивалент:
SELECT ... WHERE x=1 OR y=2
| не является коммутативной операцией, так как могут быть сгенерированы разные (хотя и эквивалентные) запросы.
XOR (^)
Объединяет два QuerySet с помощью оператора SQL XOR. Выражение XOR соответствует строкам, которые соответствуют нечетному количеству операндов.
Следующее эквивалентно:
Model.objects.filter(x=1) ^ Model.objects.filter(y=2) from django.db.models import Q Model.objects.filter(Q(x=1) ^ Q(y=2))
SQL эквивалент:
SELECT ... WHERE x=1 XOR y=2
Примечание
XOR нативно поддерживается в MariaDB и MySQL. В других базах данных x ^ y ^ ... ^ z преобразуется в эквивалент:
(x OR y OR ... OR z) AND
1=MOD(
(CASE WHEN x THEN 1 ELSE 0 END) +
(CASE WHEN y THEN 1 ELSE 0 END) +
...
(CASE WHEN z THEN 1 ELSE 0 END),
2
)
В более ранних версиях, в базах данных без нативной поддержки оператора SQL XOR, XOR возвращало строки, которые соответствовали ровно одному операнду. Предыдущее поведение не соответствовало поведению MySQL, MariaDB и Python.
Методы, которые не возвращают QuerySet
Следующие методы QuerySet оценивают QuerySet и возвращают что-то кроме QuerySet.
Эти методы не используют кеш (см. Кеширование и QuerySet). Вместо этого они каждый раз обращаются к базе данных.
Поскольку эти методы оценивают QuerySet, они являются блокирующими вызовами, и поэтому их основные (синхронные) версии нельзя вызывать из асинхронного кода. По этой причине каждый имеет соответствующую асинхронную версию с префиксом a, например, вместо get(…) вы можете использовать await aget(…).
Как правило, нет разницы в поведении, кроме их асинхронной природы, но любые различия указаны ниже рядом с каждым методом.
get()
-
get(*args, **kwargs)
-
aget(*args, **kwargs)
Асинхронная версия: aget()
Возвращает объект, соответствующий заданным параметрам поиска, которые должны быть в формате, описанном в Поисках по полям. Вы должны использовать поиски, которые гарантированно уникальны, такие как первичный ключ или поля в уникальном ограничении. Например:
Entry.objects.get(id=1) Entry.objects.get(Q(blog=blog) & Q(entry_number=1))
Если вы ожидаете, что запрос уже вернет одну строку, вы можете использовать get() без аргументов, чтобы вернуть объект для этой строки:
Entry.objects.filter(pk=1).get()
Если get() не найдет объект, он генерирует исключение Model.DoesNotExist:
Entry.objects.get(id=-999) # raises Entry.DoesNotExist
Если get() найдет более одного объекта, он генерирует исключение Model.MultipleObjectsReturned:
Entry.objects.get(name="A Duplicated Name") # raises Entry.MultipleObjectsReturned
Оба этих класса исключений являются атрибутами класса модели и специфичны для этой модели. Если вы хотите обработать такие исключения из нескольких get() вызовов для разных моделей, вы можете использовать их общие базовые классы. Например, вы можете использовать django.core.exceptions.ObjectDoesNotExist для обработки исключений DoesNotExist из нескольких моделей:
from django.core.exceptions import ObjectDoesNotExist
try:
blog = Blog.objects.get(id=1)
entry = Entry.objects.get(blog=blog, entry_number=1)
except ObjectDoesNotExist:
print("Either the blog or entry doesn't exist.")
create()
-
create(**kwargs)
-
acreate(**kwargs)
Асинхронная версия: acreate()
Удобный метод для создания объекта и сохранения его в одном шаге. Таким образом:
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
и:
p = Person(first_name="Bruce", last_name="Springsteen") p.save(force_insert=True)
эквивалентны.
Параметр force_insert описан в другом месте, но это означает, что новый объект всегда будет создан. Как правило, вам не нужно беспокоиться об этом. Однако, если ваша модель содержит значение первичного ключа, заданное вручную, и если это значение уже существует в базе данных, вызов create() завершится ошибкой IntegrityError, так как первичные ключи должны быть уникальными. Будьте готовы обработать исключение, если вы используете значения первичных ключей, заданные вручную.
get_or_create()
-
get_or_create(defaults=None, **kwargs)
-
aget_or_create(defaults=None, **kwargs)
Асинхронная версия: aget_or_create()
Удобный метод для поиска объекта с заданными kwargs (может быть пустым, если ваша модель имеет значения по умолчанию для всех полей), создавая его при необходимости.
Возвращает кортеж (object, created), где object - полученный или созданный объект, а created - булево значение, указывающее, был ли создан новый объект.
Это призвано предотвратить создание дублирующихся объектов при одновременных запросах и служит сокращением для громоздкого кода. Например:
try:
obj = Person.objects.get(first_name="John", last_name="Lennon")
except Person.DoesNotExist:
obj = Person(first_name="John", last_name="Lennon", birthday=date(1940, 10, 9))
obj.save()
Здесь, при одновременных запросах, могут быть предприняты несколько попыток сохранения Person с одинаковыми параметрами. Чтобы избежать этой проблемы гонки, вышеприведенный пример можно переписать с использованием get_or_create() следующим образом:
obj, created = Person.objects.get_or_create(
first_name="John",
last_name="Lennon",
defaults={"birthday": date(1940, 10, 9)},
)
Любые ключевые аргументы, переданные в get_or_create() — кроме необязательного, с именем defaults — будут использованы в вызове get(). Если объект найден, get_or_create() возвращает кортеж этого объекта и False.
Предупреждение
Этот метод предполагает атомарность, исходя из того, что база данных обеспечивает уникальность ключевых аргументов (см. unique или unique_together). Если поля, используемые в ключевых аргументах, не имеют ограничения уникальности, одновременные вызовы этого метода могут привести к вставке нескольких строк с одинаковыми параметрами.
Вы можете указать более сложные условия для извлеченного объекта, объединив get_or_create() с filter() и используя Q objects. Например, чтобы получить Роберта или Боба Марли, если любой из них существует, и создать последнего в противном случае:
from django.db.models import Q
obj, created = Person.objects.filter(
Q(first_name="Bob") | Q(first_name="Robert"),
).get_or_create(last_name="Marley", defaults={"first_name": "Bob"})
Если найдено несколько объектов, get_or_create() генерирует исключение MultipleObjectsReturned. Если объект не найден, get_or_create() создаст и сохранит новый объект, вернув кортеж нового объекта и True. Новый объект будет создан примерно по следующему алгоритму:
params = {k: v for k, v in kwargs.items() if "__" not in k}
params.update({k: v() if callable(v) else v for k, v in defaults.items()})
obj = self.model(**params)
obj.save()
Проще говоря, начните с любого не-'defaults' ключевого аргумента, который не содержит двойных подчеркиваний (что указывает на неточный поиск). Затем добавьте содержимое defaults, переопределяя ключи при необходимости, и используйте результат в качестве ключевых аргументов для класса модели. Если в defaults есть вызываемые объекты, оцените их. Как уже упоминалось, это упрощенное описание используемого алгоритма, но оно содержит все существенные детали. Внутренняя реализация имеет больше проверок ошибок и обрабатывает дополнительные граничные условия; если вас интересует, прочитайте код.
Если у вас есть поле с именем defaults и вы хотите использовать его как точный поиск в get_or_create(), используйте 'defaults__exact', как показано ниже:
Foo.objects.get_or_create(defaults__exact="bar", defaults={"defaults": "baz"})
Метод get_or_create() имеет аналогичное поведение обработки ошибок к create() при использовании вручную заданных первичных ключей. Если необходимо создать объект, а ключ уже существует в базе данных, будет вызвано исключение IntegrityError.
Наконец, несколько слов об использовании get_or_create() в представлениях Django. Пожалуйста, используйте его только в POST запросах, если у вас нет веской причины не делать этого. GET запросы не должны оказывать никакого влияния на данные. Вместо этого используйте POST всякий раз, когда запрос на страницу имеет побочные эффекты на ваши данные. Для получения дополнительной информации см. Безопасные методы в спецификации HTTP.
Предупреждение
Вы можете использовать get_or_create() через атрибуты ManyToManyField и обратные отношения. В этом случае вы будете ограничивать запросы в контексте этого отношения. Это может привести к проблемам целостности, если вы не используете его последовательно.
Рассмотрим следующие модели:
class Chapter(models.Model):
title = models.CharField(max_length=255, unique=True)
class Book(models.Model):
title = models.CharField(max_length=256)
chapters = models.ManyToManyField(Chapter)
Вы можете использовать get_or_create() через поле глав в книге, но оно извлекает данные только в контексте этой книги:
>>> book = Book.objects.create(title="Ulysses") >>> book.chapters.get_or_create(title="Telemachus") (<Chapter: Telemachus>, True) >>> book.chapters.get_or_create(title="Telemachus") (<Chapter: Telemachus>, False) >>> Chapter.objects.create(title="Chapter 1") <Chapter: Chapter 1> >>> book.chapters.get_or_create(title="Chapter 1") # Raises IntegrityError
Это происходит потому, что он пытается получить или создать «Главу 1» через книгу «Улисс», но не может сделать ни того, ни другого: отношение не может извлечь эту главу, потому что она не связана с этой книгой, и не может ее создать, потому что поле title должно быть уникальным.
update_or_create()
-
update_or_create(defaults=None, create_defaults=None, **kwargs)
-
aupdate_or_create(defaults=None, create_defaults=None, **kwargs)
Асинхронная версия: aupdate_or_create()
Удобный метод для обновления объекта заданным kwargs, создавая новый, если необходимо. Оба create_defaults и defaults являются словарями пар (поле, значение). Значения в create_defaults и defaults могут быть вызовами функций. defaults используется для обновления объекта, а create_defaults — для создания. Если create_defaults не указано, defaults будет использоваться для создания.
Возвращает кортеж (object, created), где object — созданный или обновлённый объект, а created — булево значение, указывающее, был ли создан новый объект.
Метод update_or_create пытается извлечь объект из базы данных на основе заданного kwargs. Если совпадение найдено, он обновляет поля, переданные в словаре defaults.
Это предназначено как сокращение для шаблонного кода. Например:
defaults = {"first_name": "Bob"}
create_defaults = {"first_name": "Bob", "birthday": date(1940, 10, 9)}
try:
obj = Person.objects.get(first_name="John", last_name="Lennon")
for key, value in defaults.items():
setattr(obj, key, value)
obj.save()
except Person.DoesNotExist:
new_values = {"first_name": "John", "last_name": "Lennon"}
new_values.update(create_defaults)
obj = Person(**new_values)
obj.save()
Этот шаблон становится довольно громоздким по мере увеличения количества полей в модели. Вышеприведённый пример можно переписать с использованием update_or_create() следующим образом:
obj, created = Person.objects.update_or_create(
first_name="John",
last_name="Lennon",
defaults={"first_name": "Bob"},
create_defaults={"first_name": "Bob", "birthday": date(1940, 10, 9)},
)
Для подробного описания того, как разрешаются имена, переданные в kwargs, см. get_or_create().
Как описано выше в get_or_create(), этот метод подвержен гонке, которая может привести к одновременной вставке нескольких строк, если уникальность не обеспечена на уровне базы данных.
Как и get_or_create() и create(), если вы используете вручную заданные первичные ключи, и объект необходимо создать, но ключ уже существует в базе данных, генерируется исключение IntegrityError.
Добавлен аргумент create_defaults.
bulk_create()
-
bulk_create(objs, batch_size=None, ignore_conflicts=False, update_conflicts=False, update_fields=None, unique_fields=None)
-
abulk_create(objs, batch_size=None, ignore_conflicts=False, update_conflicts=False, update_fields=None, unique_fields=None)
Асинхронная версия: abulk_create()
Этот метод вставляет предоставленный список объектов в базу данных эффективным способом (обычно только 1 запрос, независимо от количества объектов) и возвращает созданные объекты в виде списка в том же порядке, что и предоставленный:
>>> objs = Entry.objects.bulk_create( ... [ ... Entry(headline="This is a test"), ... Entry(headline="This is only a test"), ... ] ... )
Однако это имеет ряд ограничений:
- Метод
save()модели не будет вызываться, и сигналыpre_saveиpost_saveне будут отправляться. - Он не работает с дочерними моделями в сценарии наследования многотабличной структуры.
- Если первичный ключ модели — это
AutoFieldиignore_conflictsравно False, атрибут первичного ключа может быть получен только на определённых базах данных (в настоящее время PostgreSQL, MariaDB и SQLite 3.35+). На других базах данных он не будет установлен. - Он не работает с взаимосвязями «многие ко многим».
-
Он преобразует
objsв список, что полностью оцениваетobjsесли это генератор. Преобразование позволяет просмотреть все объекты, чтобы любые объекты с вручную заданным первичным ключом можно было вставить в первую очередь. Если вы хотите вставлять объекты партиями, не оценивая весь генератор сразу, вы можете использовать эту технику, если объекты не имеют вручную заданных первичных ключей:from itertools import islice batch_size = 100 objs = (Entry(headline="Test %s" % i) for i in range(1000)) while True: batch = list(islice(objs, batch_size)) if not batch: break Entry.objects.bulk_create(batch, batch_size)
Параметр batch_size управляет количеством объектов, создаваемых в одном запросе. По умолчанию все объекты создаются в одной партии, за исключением SQLite, где значение по умолчанию — не более 999 переменных на запрос.
На базах данных, которые его поддерживают (все кроме Oracle), установка параметра ignore_conflicts в True сообщает базе данных игнорировать ошибки при вставке строк, которые нарушают ограничения, такие как дублирование уникальных значений.
На базах данных, которые его поддерживают (все кроме Oracle), установка параметра update_conflicts в True, сообщает базе данных обновить update_fields при сбое вставки строки при конфликтах. В PostgreSQL и SQLite, в дополнение к update_fields, должен быть предоставлен список unique_fields , которые могут быть в конфликте.
Включение параметра ignore_conflicts отключает установку первичного ключа для каждого экземпляра модели (если база данных его обычно поддерживает).
В более старых версиях включение параметра update_conflicts предотвращало установку первичного ключа для каждого экземпляра модели.
Предупреждение
В MySQL и MariaDB установка параметра ignore_conflicts в True преобразует некоторые типы ошибок, кроме дублирования ключа, в предупреждения. Даже в режиме строгости. Например: неверные значения или нарушения не допускающие значения NULL. Смотрите документацию MySQL по ссылке и документацию MariaDB по ссылке для получения дополнительной информации.
bulk_update()
-
bulk_update(objs, fields, batch_size=None)
-
abulk_update(objs, fields, batch_size=None)
Асинхронная версия: abulk_update()
Этот метод эффективно обновляет указанные поля в предоставленных экземплярах модели, как правило, одним запросом, и возвращает количество обновленных объектов:
>>> objs = [ ... Entry.objects.create(headline="Entry 1"), ... Entry.objects.create(headline="Entry 2"), ... ] >>> objs[0].headline = "This is entry 1" >>> objs[1].headline = "This is entry 2" >>> Entry.objects.bulk_update(objs, ["headline"]) 2
QuerySet.update() используется для сохранения изменений, поэтому это более эффективно, чем перебор списка моделей и вызов save() для каждой из них, но у него есть несколько ограничений:
- Вы не можете обновить первичный ключ модели.
- Метод
save()каждой модели не вызывается, и сигналыpre_saveиpost_saveне отправляются. - Если обновляется большое количество столбцов в большом количестве строк, сгенерированный SQL-запрос может быть очень большим. Избегайте этого, указав подходящий параметр
batch_size. - Обновление полей, определённых в родителях наследования многотабличной структуры, вызовет дополнительный запрос на каждый родитель.
- Если в отдельной партии содержатся дубликаты, только первый экземпляр в этой партии приведёт к обновлению.
- Количество обновлённых объектов, возвращаемых функцией, может быть меньше количества переданных объектов. Это может быть связано с дубликатами переданных объектов, обновлёнными в одной партии или с проблемами гонки, из-за которых объекты больше не присутствуют в базе данных.
Параметр batch_size управляет количеством объектов, сохраняемых в одном запросе. По умолчанию все объекты обновляются в одной партии, за исключением SQLite и Oracle, у которых есть ограничения на количество переменных в запросе.
count()
-
count()
-
acount()
Асинхронная версия: acount()
Возвращает целое число, представляющее количество объектов в базе данных, соответствующих QuerySet.
Пример:
# Returns the total number of entries in the database. Entry.objects.count() # Returns the number of entries whose headline contains 'Lennon' Entry.objects.filter(headline__contains="Lennon").count()
Вызов count() выполняет SELECT COUNT(*) за кулисами, поэтому всегда следует использовать count(), а не загружать все записи в объекты Python и вызывать len() на результате (если вам не нужно загружать объекты в память, в этом случае len() будет быстрее).
Обратите внимание, что если вам нужно количество элементов в QuerySet и вы также получаете экземпляры модели из него (например, итерацией по нему), вероятно, более эффективно использовать len(queryset), что не вызовет дополнительного запроса к базе данных, как count().
Если набор запросов уже полностью получен, count() использует эту длину, а не выполняет дополнительный запрос к базе данных.
in_bulk()
-
in_bulk(id_list=None, *, field_name='pk')
-
ain_bulk(id_list=None, *, field_name='pk')
Асинхронная версия: ain_bulk()
Принимает список значений поля (id_list) и field_name для этих значений и возвращает словарь, сопоставляющий каждое значение с экземпляром объекта с заданным значением поля. Исключение django.core.exceptions.ObjectDoesNotExist никогда не будет генерироваться in_bulk; то есть любое значение id_list , не соответствующее ни одному экземпляру, просто будет проигнорировано. Если id_list не указано, возвращаются все объекты из набора запросов. field_name должно быть уникальным полем или отдельным полем (если в distinct() указано только одно поле). field_name по умолчанию — первичный ключ.
Пример:
>>> Blog.objects.in_bulk([1])
{1: <Blog: Beatles Blog>}
>>> Blog.objects.in_bulk([1, 2])
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>}
>>> Blog.objects.in_bulk([])
{}
>>> Blog.objects.in_bulk()
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>, 3: <Blog: Django Weblog>}
>>> Blog.objects.in_bulk(["beatles_blog"], field_name="slug")
{'beatles_blog': <Blog: Beatles Blog>}
>>> Blog.objects.distinct("name").in_bulk(field_name="name")
{'Beatles Blog': <Blog: Beatles Blog>, 'Cheddar Talk': <Blog: Cheddar Talk>, 'Django Weblog': <Blog: Django Weblog>}
Если вы передадите in_bulk() пустой список, получите пустой словарь.
iterator()
-
iterator(chunk_size=None)
-
aiterator(chunk_size=None)
Асинхронная версия: aiterator()
Оценивает QuerySet (выполняя запрос) и возвращает итератор (см. PEP 234) по результатам или асинхронный итератор (см. PEP 492), если вы вызываете его асинхронную версию aiterator.
Блок 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.
В зависимости от используемого бэкенда базы данных результаты запроса будут загружены либо сразу, либо будут передаваться из базы данных с помощью курсоров на стороне сервера.
С курсорами на стороне сервера
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")
earliest()
-
earliest(*fields)
-
aearliest(*fields)
Асинхронная версия: aearliest()
По-другому работает как latest(), но направление меняется.
first()
-
first()
-
afirst()
Асинхронная версия: afirst()
Возвращает первый объект, соответствующий запросу, или None, если соответствующего объекта нет. Если у QuerySet нет сортировки, то QuerySet автоматически сортируется по первичному ключу. Это может повлиять на результаты агрегирования, как описано в взаимодействии с order_by().
Пример:
p = Article.objects.order_by("title", "pub_date").first()
Обратите внимание, что first() — это удобный метод; следующий пример кода эквивалентен предыдущему:
try:
p = Article.objects.order_by("title", "pub_date")[0]
except IndexError:
p = None
last()
-
last()
-
alast()
Асинхронная версия: alast()
Работает как first(), но возвращает последний объект в QuerySet.
aggregate()
-
aggregate(*args, **kwargs)
-
aaggregate(*args, **kwargs)
Асинхронная версия: aaggregate()
Возвращает словарь агрегированных значений (средних, сумм и т. д.) по QuerySet. Каждый аргумент к aggregate() определяет значение, которое будет включено в возвращаемый словарь.
Функции агрегирования, предоставляемые Django, описаны в разделе Функции агрегирования ниже. Поскольку агрегаты также являются выражениями запросов, вы можете комбинировать агрегаты с другими агрегатами или значениями для создания сложных агрегатов.
Агрегаты, указанные с использованием ключевых аргументов, будут использовать ключевое слово в качестве имени для аннотации. Анонимные аргументы будут иметь имя, сгенерированное на основе имени функции агрегирования и поля модели, которое агрегируется. Сложные агрегаты не могут использовать анонимные аргументы и должны указывать ключевой аргумент в качестве псевдонима.
Например, когда вы работаете со статьями блога, вам может потребоваться узнать количество авторов, которые внесли вклад в статьи блога:
>>> from django.db.models import Count
>>> Blog.objects.aggregate(Count("entry"))
{'entry__count': 16}
Используя ключевой аргумент для указания функции агрегирования, вы можете контролировать имя возвращаемого значения агрегирования:
>>> Blog.objects.aggregate(number_of_entries=Count("entry"))
{'number_of_entries': 16}
Подробный обзор агрегирования см. в руководстве по теме агрегирования.
exists()
-
exists()
-
aexists()
Асинхронная версия: aexists()
Возвращает True если QuerySet содержит результаты, и False в противном случае. Это пытается выполнить запрос самым простым и быстрым способом, но при этом выполняет практически такой же запрос, как и обычный запрос QuerySet.
exists() полезно для поиска, связанного с существованием каких-либо объектов в QuerySet, особенно в контексте большого QuerySet.
Чтобы проверить, содержит ли запрос какие-либо элементы:
if some_queryset.exists():
print("There is at least one object in some_queryset")
Что будет быстрее, чем:
if some_queryset:
print("There is at least one object in some_queryset")
… но не на большой величине (поэтому для повышения эффективности требуется большой запрос).
Кроме того, если some_queryset ещё не был вычислен, но вы знаете, что он будет вычислен в какой-то момент, то использование some_queryset.exists() займёт больше времени в целом (один запрос для проверки существования плюс дополнительный запрос для последующего получения результатов), чем использование bool(some_queryset), которое получает результаты, а затем проверяет, были ли возвращены какие-либо результаты.
contains()
-
contains(obj)
-
acontains(obj)
Асинхронная версия: acontains()
Возвращает True если QuerySet содержит obj, и False в противном случае. Это пытается выполнить запрос самым простым и быстрым способом.
contains() полезно для проверки принадлежности объекта к QuerySet, особенно в контексте большого QuerySet.
Чтобы проверить, содержит ли запрос определённый элемент:
if some_queryset.contains(obj):
print("Entry contained in queryset")
Это будет быстрее, чем следующее, которое требует вычисления и итерации по всему запросу:
if obj in some_queryset:
print("Entry contained in queryset")
Подобно exists(), если some_queryset ещё не был вычислен, но вы знаете, что он будет вычислен в какой-то момент, то использование some_queryset.contains(obj) выполнит дополнительный запрос к базе данных, что обычно приводит к более низкой общей производительности.
update()
-
update(**kwargs)
-
aupdate(**kwargs)
Асинхронная версия: aupdate()
Выполняет запрос SQL для обновления указанных полей и возвращает количество изменённых строк (что может не совпадать с количеством обновлённых строк, если некоторые строки уже имеют новое значение).
Например, чтобы отключить комментарии для всех записей блога, опубликованных в 2010 году, можно сделать так:
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
(Предполагается, что ваша модель Entry имеет поля pub_date и comments_on.)
Вы можете обновить несколько полей — ограничений по количеству нет. Например, здесь мы обновляем поля comments_on и headline:
>>> Entry.objects.filter(pub_date__year=2010).update( ... comments_on=False, headline="This is old" ... )
Метод update() применяется немедленно, и единственное ограничение для QuerySet, который обновляется, заключается в том, что он может обновлять только столбцы в основной таблице модели, а не в связанных моделях. Вы не можете сделать это, например:
>>> Entry.objects.update(blog__name="foo") # Won't work!
Однако фильтрация по связанным полям всё ещё возможна:
>>> Entry.objects.filter(blog__id=1).update(comments_on=True)
Вы не можете вызвать update() на QuerySet, на котором был взят срез или который иным образом больше не может быть отфильтрован.
Метод update() возвращает количество затронутых строк:
>>> Entry.objects.filter(id=64).update(comments_on=True) 1 >>> Entry.objects.filter(slug="nonexistent-slug").update(comments_on=True) 0 >>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False) 132
Если вы просто обновляете запись и вам не нужно ничего делать с объектом модели, наиболее эффективным подходом является вызов update(), а не загрузка объекта модели в память. Например, вместо этого:
e = Entry.objects.get(id=10) e.comments_on = False e.save()
… сделайте так:
Entry.objects.filter(id=10).update(comments_on=False)
Использование update() также предотвращает состояние гонки, когда что-то может измениться в базе данных в короткий промежуток времени между загрузкой объекта и вызовом save().
Наконец, обратите внимание, что update() выполняет обновление на уровне SQL и, таким образом, не вызывает никаких методов save() для ваших моделей, а также не излучает сигналы pre_save или post_save (которые являются следствием вызова Model.save()). Если вы хотите обновить множество записей для модели, у которой есть пользовательский метод save(), переберите их и вызовите save(), как показано ниже:
for e in Entry.objects.filter(pub_date__year=2010):
e.comments_on = False
e.save()
Упорядоченный запрос
Цепочечное соединение order_by() с update() поддерживается только в MariaDB и MySQL и игнорируется для других баз данных. Это полезно для обновления уникального поля в указанном порядке без конфликтов. Например:
Entry.objects.order_by("-number").update(number=F("number") + 1)
Примечание
order_by() условие будет проигнорировано, если оно содержит аннотации, унаследованные поля или запросы, охватывающие отношения.
delete()
-
delete()
-
adelete()
Асинхронная версия: adelete()
Выполняет запрос SQL для удаления всех строк в QuerySet и возвращает количество удалённых объектов и словарь с количеством удалений по типу объекта.
delete() применяется немедленно. Вы не можете вызвать delete() на QuerySet, на котором был взят срез или который иным образом больше не может быть отфильтрован.
Например, чтобы удалить все записи в определённом блоге:
>>> b = Blog.objects.get(pk=1)
# Delete all the entries belonging to this Blog.
>>> Entry.objects.filter(blog=b).delete()
(4, {'blog.Entry': 2, 'blog.Entry_authors': 2})
По умолчанию 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 не препятствуют использованию быстрого пути при удалении.
Обратите внимание, что запросы, сгенерированные при удалении объекта, являются деталями реализации и могут меняться.
as_manager()
-
classmethod as_manager()
Метод класса, который возвращает экземпляр Manager с копией методов QuerySet. См. Создание менеджера с методами QuerySet для получения более подробной информации.
Обратите внимание, что в отличие от других записей в этом разделе, у него нет асинхронной версии, так как он не выполняет запрос.
explain()
-
explain(format=None, **options)
-
aexplain(format=None, **options)
Асинхронная версия: aexplain()
Возвращает строку плана выполнения QuerySet запроса, которая описывает, как база данных выполнит запрос, включая используемые индексы или объединения. Знание этих деталей может помочь вам улучшить производительность медленных запросов.
Например, при использовании PostgreSQL:
>>> print(Blog.objects.filter(title="My Blog").explain()) Seq Scan on blog (cost=0.00..35.50 rows=10 width=12) Filter: (title = 'My Blog'::bpchar)
Вывод существенно отличается между базами данных.
explain() поддерживается всеми встроенными бэкендами баз данных, кроме Oracle, поскольку реализация там не очевидна.
Параметр format изменяет формат вывода из значения по умолчанию базы данных, который обычно основан на тексте. PostgreSQL поддерживает форматы 'TEXT', 'JSON', 'YAML', и 'XML'. MariaDB и MySQL поддерживают форматы 'TEXT' (также называемый 'TRADITIONAL') и 'JSON'. MySQL 8.0.16+ также поддерживает улучшенный формат 'TREE', похожий на вывод 'TEXT' PostgreSQL и используемый по умолчанию, если поддерживается.
Некоторые базы данных принимают флаги, которые могут вернуть больше информации о запросе. Передайте эти флаги в качестве ключевых аргументов. Например, при использовании PostgreSQL:
>>> print(Blog.objects.filter(title="My Blog").explain(verbose=True, analyze=True)) Seq Scan on public.blog (cost=0.00..35.50 rows=10 width=12) (actual time=0.004..0.004 rows=10 loops=1) Output: id, title Filter: (blog.title = 'My Blog'::bpchar) Planning time: 0.064 ms Execution time: 0.058 ms
В некоторых базах данных флаги могут привести к выполнению запроса, что может негативно повлиять на вашу базу данных. Например, флаг ANALYZE, поддерживаемый MariaDB, MySQL 8.0.18+ и PostgreSQL, может привести к изменениям в данных, если существуют триггеры или вызывается функция, даже для запроса SELECT.
Добавлена поддержка опции generic_plan в PostgreSQL 16+.
Field поиск
Поиск по полям — это способ указать основную часть SQL-операторной WHERE строки. Они задаются в виде ключевых аргументов для методов QuerySet filter(), exclude() и get().
Для введения см. документацию по моделям и запросам к базе данных.
Встроенные в Django поисковые выражения перечислены ниже. Также можно написать пользовательские поисковые выражения для полей модели.
Для удобства, когда тип поиска не указан (как в Entry.objects.get(id=14)), тип поиска предполагается как exact.
exact
Точное соответствие. Если предоставленное значение для сравнения — None, оно будет интерпретироваться как SQL-оператор NULL (см. isnull для получения более подробной информации).
Примеры:
Entry.objects.get(id__exact=14) Entry.objects.get(id__exact=None)
SQL эквиваленты:
SELECT ... WHERE id = 14; SELECT ... WHERE id IS NULL;
Сравнения в MySQL
В MySQL параметр «сопоставление» таблицы базы данных определяет, являются ли сравнения exact чувствительными к регистру. Это параметр базы данных, а не параметр Django. Можно настроить таблицы MySQL для использования сравнений, чувствительных к регистру, но это может иметь свои недостатки. Подробнее об этом см. в разделе сопоставление в документации баз данных.
iexact
Нечувствительное к регистру точное соответствие. Если предоставленное значение для сравнения — None, оно будет интерпретироваться как SQL-оператор NULL (см. isnull для получения более подробной информации).
Пример:
Blog.objects.get(name__iexact="beatles blog") Blog.objects.get(name__iexact=None)
SQL эквиваленты:
SELECT ... WHERE name ILIKE 'beatles blog'; SELECT ... WHERE name IS NULL;
Обратите внимание, что первый запрос будет соответствовать 'Beatles Blog', 'beatles blog', 'BeAtLes BLoG', и т.д.
Пользователи SQLite
При использовании бэкэнда SQLite и не-ASCII строк, помните о примечании к базе данных относительно сравнений строк. SQLite не выполняет нечувствительное к регистру сопоставление для не-ASCII строк.
contains
Чувствительное к регистру проверка наличия.
Пример:
Entry.objects.get(headline__contains="Lennon")
SQL эквивалент:
SELECT ... WHERE headline LIKE '%Lennon%';
Обратите внимание, что это будет соответствовать заголовку 'Lennon honored today', но не 'lennon
honored today'.
Пользователи SQLite
SQLite не поддерживает чувствительные к регистру запросы LIKE; contains действует как icontains для SQLite. См. примечание к базе данных для получения более подробной информации.
icontains
Нечувствительное к регистру проверка наличия.
Пример:
Entry.objects.get(headline__icontains="Lennon")
SQL эквивалент:
SELECT ... WHERE headline ILIKE '%Lennon%';
Пользователи SQLite
При использовании бэкэнда SQLite и не-ASCII строк, имейте в виду примечание к базе данных о сравнении строк.
in
В заданном итерируемом объекте; часто список, кортеж или набор запросов. Это не типичный случай использования, но строки (являясь итерируемыми объектами) принимаются.
Примеры:
Entry.objects.filter(id__in=[1, 3, 4]) Entry.objects.filter(headline__in="abc")
SQL эквиваленты:
SELECT ... WHERE id IN (1, 3, 4);
SELECT ... WHERE headline IN ('a', 'b', 'c');
Вы также можете использовать набор запросов для динамического оценки списка значений вместо предоставления списка литеральных значений:
inner_qs = Blog.objects.filter(name__contains="Cheddar") entries = Entry.objects.filter(blog__in=inner_qs)
Этот набор запросов будет оцениваться как подзапрос:
SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')
Если вы передаёте QuerySet, полученный из values() или values_list(), в качестве значения для поиска __in, убедитесь, что вы извлекаете только одно поле в результате. Например, это будет работать (фильтрация по названиям блогов):
inner_qs = Blog.objects.filter(name__contains="Ch").values("name")
entries = Entry.objects.filter(blog__name__in=inner_qs)
Этот пример вызовет исключение, так как внутренний запрос пытается извлечь два значения поля, где ожидается только одно:
# Bad code! Will raise a TypeError.
inner_qs = Blog.objects.filter(name__contains="Ch").values("name", "id")
entries = Entry.objects.filter(blog__name__in=inner_qs)
Учёт производительности
Будьте осторожны при использовании вложенных запросов и понимайте характеристики производительности вашего сервера базы данных (при необходимости проведите тестирование!). Некоторые бэкэнды баз данных, особенно MySQL, не оптимизируют вложенные запросы очень хорошо. В таких случаях эффективнее извлечь список значений и затем передать его во второй запрос. То есть, выполните два запроса вместо одного:
values = Blog.objects.filter(name__contains="Cheddar").values_list("pk", flat=True)
entries = Entry.objects.filter(blog__in=list(values))
Обратите внимание на вызов list() вокруг Blog QuerySet для принудительного выполнения первого запроса. Без него выполнялся бы вложенный запрос, потому что наборы запросов ленивые.
gt
Больше чем.
Пример:
Entry.objects.filter(id__gt=4)
SQL эквивалент:
SELECT ... WHERE id > 4;
gte
Больше или равно.
lt
Меньше чем.
lte
Меньше или равно.
startswith
Чувствительное к регистру начинается с.
Пример:
Entry.objects.filter(headline__startswith="Lennon")
SQL эквивалент:
SELECT ... WHERE headline LIKE 'Lennon%';
SQLite не поддерживает чувствительные к регистру запросы LIKE; startswith работает как istartswith для SQLite.
istartswith
Нечувствительное к регистру начинается с.
Пример:
Entry.objects.filter(headline__istartswith="Lennon")
SQL эквивалент:
SELECT ... WHERE headline ILIKE 'Lennon%';
Пользователи SQLite
При использовании бэкэнда SQLite и не-ASCII строк, имейте в виду примечание к базе данных о сравнении строк.
endswith
Чувствительное к регистру заканчивается на.
Пример:
Entry.objects.filter(headline__endswith="Lennon")
SQL эквивалент:
SELECT ... WHERE headline LIKE '%Lennon';
Пользователи SQLite
SQLite не поддерживает чувствительные к регистру запросы LIKE; endswith работает как iendswith для SQLite. См. примечание к базе данных для более подробной информации.
iendswith
Нечувствительное к регистру заканчивается на.
Пример:
Entry.objects.filter(headline__iendswith="Lennon")
SQL эквивалент:
SELECT ... WHERE headline ILIKE '%Lennon'
Пользователи SQLite
При использовании бэкэнда SQLite и не-ASCII строк, имейте в виду примечание к базе данных о сравнении строк.
range
Проверка диапазона (включительно).
Пример:
import datetime start_date = datetime.date(2005, 1, 1) end_date = datetime.date(2005, 3, 31) Entry.objects.filter(pub_date__range=(start_date, end_date))
SQL эквивалент:
SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';
Вы можете использовать range везде, где вы можете использовать BETWEEN в SQL — для дат, чисел и даже символов.
Предупреждение
Фильтрация DateTimeField с датами не будет включать элементы в последний день, потому что границы интерпретируются как «0:00 в указанную дату». Если pub_date был DateTimeField, вышеприведенное выражение будет преобразовано в следующий SQL:
SELECT ... WHERE pub_date BETWEEN '2005-01-01 00:00:00' and '2005-03-31 00:00:00';
Как правило, нельзя смешивать даты и datetime.
date
Для полей datetime преобразует значение как дату. Позволяет цеплять дополнительные поисковые выражения по полям. Принимает значение даты.
Пример:
Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1)) Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
(Эквивалент SQL-фрагмента для этого поиска не включён, поскольку реализация соответствующего запроса варьируется между различными движками баз данных.)
Когда USE_TZ установлено в True, поля преобразуются в текущую часовую зону перед фильтрацией. Это требует определения часовых зон в базе данных.
year
Для полей даты и 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’s re.
Пример:
Entry.objects.get(title__regex=r"^(An?|The) +")
Эквиваленты SQL:
SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL SELECT ... WHERE REGEXP_LIKE(title, '^(An?|The) +', 'c'); -- Oracle SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite
Использование сырых строк (например, r'foo' вместо 'foo') для передачи синтаксиса регулярного выражения рекомендуется.
iregex
Нечувствительный к регистру поиск по регулярному выражению.
Пример:
Entry.objects.get(title__iregex=r"^(an?|the) +")
Эквиваленты SQL:
SELECT ... WHERE title REGEXP '^(an?|the) +'; -- MySQL SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'i'); -- Oracle SELECT ... WHERE title ~* '^(an?|the) +'; -- PostgreSQL SELECT ... WHERE title REGEXP '(?i)^(an?|the) +'; -- SQLite
Функции агрегации
Django предоставляет следующие функции агрегации в модуле django.db.models. Подробнее о том, как использовать эти функции агрегации, см. руководство по темам агрегации. Смотрите документацию Aggregate, чтобы узнать, как создавать свои агрегаты.
Предупреждение
SQLite не может обрабатывать агрегацию по полям даты/времени прямо. Это происходит потому, что в SQLite нет встроенных полей даты/времени, и Django в настоящее время эмулирует эти возможности с помощью текстового поля. Попытки использовать агрегацию по полям даты/времени в SQLite приведут к ошибке NotSupportedError.
Пустые наборы запросов или группы
Функции агрегации возвращают None при использовании с пустым QuerySet или группой. Например, функция агрегации Sum возвращает None вместо 0, если QuerySet не содержит записей или для любой пустой группы в непустом QuerySet. Чтобы вернуть другое значение вместо этого, определите аргумент default. Count является исключением из этого поведения; он возвращает 0, если QuerySet пусто, так как Count не поддерживает аргумент default.
У всех агрегатов есть общие параметры:
expressions
Строки, которые ссылаются на поля модели, преобразования поля или выражения запроса.
output_field
Необязательный аргумент, представляющий поле модели возвращаемого значения
Примечание
При объединении нескольких типов полей Django может определить output_field только если все поля одного типа. В противном случае вы должны предоставить output_field самостоятельно.
filter
Необязательный Q object, который используется для фильтрации строк, которые агрегируются.
См. Условную агрегацию и Фильтрацию по аннотациям для примеров использования.
default
Необязательный аргумент, позволяющий указать значение, используемое в качестве значения по умолчанию, когда набор запросов (или группировка) не содержит записей.
**extra
Ключевые аргументы, которые могут предоставить дополнительный контекст для SQL, сгенерированного агрегатом.
Avg
-
class Avg(expression, output_field=None, distinct=False, filter=None, default=None, **extra)[source] -
Возвращает среднее значение заданного выражения, которое должно быть числовым, если вы не укажете другое
output_field.- По умолчанию псевдоним:
<field>__avg - Тип возвращаемого значения:
floatесли входной типint, в противном случае — такое же, как поле ввода, илиoutput_fieldесли предоставлено. Если набор запросов или группировка пуста, возвращаетсяdefault.
-
distinct -
Необязательно. Если
distinct=True,Avgвозвращает среднее значение уникальных значений. Это эквивалент SQLAVG(DISTINCT <field>). Значение по умолчаниюFalse.
- По умолчанию псевдоним:
Count
-
class Count(expression, distinct=False, filter=None, **extra)[source] -
Возвращает количество объектов, связанных через указанное выражение.
Count('*')эквивалентно выражению SQLCOUNT(*).- По умолчанию псевдоним:
<field>__count - Тип возвращаемого значения:
int
-
distinct -
Необязательно. Если
distinct=True, подсчет будет включать только уникальные экземпляры. Это эквивалент SQLCOUNT(DISTINCT <field>). Значение по умолчаниюFalse.
Примечание
Аргумент
defaultне поддерживается. - По умолчанию псевдоним:
Max
-
class Max(expression, output_field=None, filter=None, default=None, **extra)[source] -
Возвращает максимальное значение заданного выражения.
- По умолчанию псевдоним:
<field>__max - Тип возвращаемого значения: такой же, как поле ввода, или
output_fieldесли предоставлено. Если набор запросов или группировка пуста, возвращаетсяdefault.
- По умолчанию псевдоним:
Min
-
class Min(expression, output_field=None, filter=None, default=None, **extra)[source] -
Возвращает минимальное значение заданного выражения.
- По умолчанию псевдоним:
<field>__min - Тип возвращаемого значения: такой же, как поле ввода, или
output_fieldесли предоставлено. Если набор запросов или группировка пуста, возвращаетсяdefault.
- По умолчанию псевдоним:
StdDev
-
class StdDev(expression, output_field=None, sample=False, filter=None, default=None, **extra)[source] -
Возвращает стандартное отклонение данных в заданном выражении.
- По умолчанию псевдоним:
<field>__stddev - Тип возвращаемого значения:
floatесли входной типint, в противном случае — такой же, как поле ввода, илиoutput_fieldесли предоставлено. Если набор запросов или группировка пуста, возвращаетсяdefault.
-
sample -
Необязательно. По умолчанию
StdDevвозвращает стандартное отклонение генеральной совокупности. Однако, еслиsample=True, возвращаемое значение будет выборочным стандартным отклонением.
- По умолчанию псевдоним:
Sum
-
class Sum(expression, output_field=None, distinct=False, filter=None, default=None, **extra)[source] -
Вычисляет сумму всех значений заданного выражения.
- По умолчанию псевдоним:
<field>__sum - Тип возвращаемого значения: такой же, как поле ввода, или
output_fieldесли предоставлено. Если набор запросов или группировка пуста, возвращаетсяdefault.
-
distinct -
Необязательно. Если
distinct=True,Sumвозвращает сумму уникальных значений. Это эквивалент SQLSUM(DISTINCT <field>). Значение по умолчаниюFalse.
- По умолчанию псевдоним:
Variance
-
class Variance(expression, output_field=None, sample=False, filter=None, default=None, **extra)[source] -
Возвращает дисперсию данных в заданном выражении.
- По умолчанию псевдоним:
<field>__variance - Тип возвращаемого значения:
floatесли входной типint, в противном случае — такой же, как поле ввода, илиoutput_fieldесли предоставлено. Если набор запросов или группировка пуста, возвращаетсяdefault.
-
sample -
Необязательно. По умолчанию
Varianceвозвращает дисперсию генеральной совокупности. Однако, еслиsample=True, возвращаемое значение будет выборочной дисперсией.
- По умолчанию псевдоним:
Инструменты, связанные с запросами
Q() объекты
-
class Q[source]
Объект Q() представляет собой SQL-условие, которое может использоваться в операциях, связанных с базой данных. Он похож на то, как объект F() представляет значение поля модели или аннотации. Они позволяют определять и повторно использовать условия. Их можно инвертировать с использованием оператора ~ (NOT) и объединять с помощью операторов, таких как | (OR), & (AND) и ^ (XOR). См. Сложные запросы с объектами Q.
Prefetch() объекты
-
class Prefetch(lookup, queryset=None, to_attr=None)[source]
Объект Prefetch() может использоваться для управления работой prefetch_related().
Аргумент lookup описывает отношения, которые необходимо отслеживать, и работает так же, как строковые выражения, переданные в prefetch_related(). Например:
>>> from django.db.models import Prefetch
>>> Question.objects.prefetch_related(Prefetch("choice_set")).get().choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
# This will only execute two queries regardless of the number of Question
# and Choice objects.
>>> Question.objects.prefetch_related(Prefetch("choice_set"))
<QuerySet [<Question: What's up?>]>
Аргумент queryset предоставляет базовый QuerySet для данного поиска. Это полезно для дальнейшего фильтрации операции предварительной выборки или вызова select_related() из предварительно выбранного отношения, тем самым ещё больше уменьшая количество запросов:
>>> voted_choices = Choice.objects.filter(votes__gt=0)
>>> voted_choices
<QuerySet [<Choice: The sky>]>
>>> prefetch = Prefetch("choice_set", queryset=voted_choices)
>>> Question.objects.prefetch_related(prefetch).get().choice_set.all()
<QuerySet [<Choice: The sky>]>
Аргумент to_attr задаёт результат операции предварительной выборки в пользовательском атрибуте:
>>> prefetch = Prefetch("choice_set", queryset=voted_choices, to_attr="voted_choices")
>>> Question.objects.prefetch_related(prefetch).get().voted_choices
[<Choice: The sky>]
>>> Question.objects.prefetch_related(prefetch).get().choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
END_OF_DOCUMENT_MARKER
Примечание
При использовании to_attr предварительно загруженный результат хранится в списке. Это может значительно повысить скорость по сравнению с традиционными prefetch_related вызовами, которые хранят кэшированный результат внутри экземпляра QuerySet.
prefetch_related_objects()
Асинхронная версия: aprefetch_related_objects()
Предварительно загружает указанные ссылки на итерацию экземпляров модели. Это полезно в коде, который получает список экземпляров моделей, а не QuerySet; например, при извлечении моделей из кэша или их ручном создании.
Передайте итерируемый список экземпляров моделей (все они должны быть одного класса) и ссылки или объекты Prefetch, которые вы хотите предварительно загрузить. Например:
>>> from django.db.models import prefetch_related_objects >>> restaurants = fetch_top_restaurants_from_cache() # A list of Restaurants >>> prefetch_related_objects(restaurants, "pizzas__toppings")
При использовании нескольких баз данных с prefetch_related_objects, запрос предварительной загрузки будет использовать базу данных, связанную с экземпляром модели. Это можно переопределить, используя пользовательский набор запросов в связанной ссылке.
aprefetch_related_objects() функция была добавлена.
FilteredRelation() объекты
-
class FilteredRelation(relation_name, *, condition=Q())[source] -
-
relation_name -
Имя поля, по которому вы хотите отфильтровать отношение.
-
condition -
Объект
Q, управляющий фильтрацией.
-
FilteredRelation используется с annotate() для создания условия ON, когда выполняется JOIN. Он не действует на стандартное отношение, а на имя аннотации (pizzas_vegetarian в примере ниже).
Например, чтобы найти рестораны, у которых есть вегетарианские пиццы с 'mozzarella' в названии:
>>> from django.db.models import FilteredRelation, Q >>> Restaurant.objects.annotate( ... pizzas_vegetarian=FilteredRelation( ... "pizzas", ... condition=Q(pizzas__vegetarian=True), ... ), ... ).filter(pizzas_vegetarian__name__icontains="mozzarella")
Если пицц много, этот набор запросов работает лучше, чем:
>>> Restaurant.objects.filter( ... pizzas__vegetarian=True, ... pizzas__name__icontains="mozzarella", ... )
потому что фильтрация в WHERE условии первого набора запросов будет работать только с вегетарианскими пиццами.
FilteredRelation не поддерживает:
-
QuerySet.only()иprefetch_related(). GenericForeignKey, унаследованный от родительской модели.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/5.1/ref/models/querysets/