Справочник по API QuerySet
В данном документе описываются подробности API QuerySet. Он основан на материалах, представленных в руководствах по моделям и запросам к базе данных, поэтому вам, вероятно, стоит прочитать и понять эти документы перед чтением данного.
В этом справочнике мы будем использовать примеры моделей веб-журнала примеры моделей Weblog, представленные в руководстве по запросам к базе данных.
Когда QuerySet оцениваются
Внутренне, QuerySet может быть построен, отфильтрован, разрезан и в целом передан без фактического обращения к базе данных. Действия с базой данных не происходят до тех пор, пока вы не выполните какие-либо действия для оценки queryset.
Вы можете оценить QuerySet следующими способами:
-
Итерация.
QuerySetявляется итерируемым, и он выполняет свой запрос к базе данных в первый раз, когда вы итерируете по нему. Например, это выведет заголовок всех записей в базе данных:for e in Entry.objects.all(): print(e.headline)Примечание: Не используйте этот метод, если всё, что вам нужно сделать, это определить, существует ли хотя бы один результат. Эффективнее использовать
exists(). -
Разрезание. Как объясняется в Ограничение QuerySet,
QuerySetможет быть разрезан с использованием синтаксиса срезов массивов Python. Разрезание неоцененногоQuerySetобычно возвращает другой неоцененныйQuerySet, но Django выполнит запрос к базе данных, если вы используете параметр «шаг» синтаксиса среза, и вернет список. Разрезание уже оцененногоQuerySetтакже возвращает список.Также обратите внимание, что, хотя срезы неоцененного
QuerySetвозвращают другой неоцененныйQuerySet, дальнейшие изменения (например, добавление дополнительных фильтров или изменение сортировки) не допускаются, так как это не хорошо переводится на SQL и не имеет ясного смысла. - Сериализация/Кеширование. Подробности о том, что происходит при сериализации QuerySet, см. в следующем разделе. Важно для целей этого раздела, что результаты считываются из базы данных.
-
repr().
QuerySetоценивается, когда вы вызываетеrepr()на нем. Это сделано для удобства в интерактивном интерпретаторе Python, чтобы вы могли сразу увидеть свои результаты при интерактивном использовании API. -
len().
QuerySetоценивается, когда вы вызываетеlen()на нем. Как можно ожидать, это возвращает длину списка результатов.Примечание: Если вам нужно только определить количество записей в наборе (и вам не нужны сами объекты), гораздо эффективнее обработать подсчет на уровне базы данных с помощью SQL
SELECT COUNT(*). Django предоставляет методcount()именно по этой причине. -
list(). Вынужденная оценка
QuerySetвызовомlist()на нем. Например:entry_list = list(Entry.objects.all())
-
bool(). Тестирование
QuerySetв контексте булевой переменной, таком как использованиеbool(),or,andили оператораif, приведет к выполнению запроса. Если существует хотя бы один результат,QuerySetимеет значениеTrue, в противном случаеFalse. Например:if Entry.objects.filter(headline="Test"): print("There is at least one Entry with the headline Test")Примечание: Если вам нужно только определить, существует ли по крайней мере один результат (и вам не нужны сами объекты), эффективнее использовать
exists().
Сериализация QuerySet
Если вы pickle QuerySet, это приведет к загрузке всех результатов в память перед сериализацией. Сериализация обычно используется как прелюдия к кэшированию, и при повторной загрузке кэшированного queryset вы хотите, чтобы результаты уже были присутствовать и готовы к использованию (чтение из базы данных может занять некоторое время, что делает кэширование бесполезным). Это означает, что при разсериализации QuerySet, она содержит результаты на момент сериализации, а не результаты, которые в настоящее время находятся в базе данных.
Если вы хотите сериализовать только необходимую информацию для повторного создания QuerySet из базы данных впоследствии, сериализуйте атрибут query QuerySet. Затем вы можете повторно создать исходный QuerySet (без загруженных результатов) с помощью кода, подобного этому:
>>> import pickle >>> query = pickle.loads(s) # Assuming 's' is the pickled string. >>> qs = MyModel.objects.all() >>> qs.query = query # Restore the original 'query'.
Атрибут query является неявным объектом. Он представляет внутренние данные построения запроса и не является частью публичного API. Однако, безопасно (и полностью поддерживается) сериализовать и десериализовать содержимое атрибута, как описано здесь.
QuerySet API
Вот формальное объявление QuerySet:
-
class QuerySet(model=None, query=None, using=None, hints=None) -
Обычно при взаимодействии с
QuerySetвы будете использовать его, цепочкой фильтров. Для того, чтобы это работало, большинство методовQuerySetвозвращают новые queryset. Эти методы подробно описаны позже в этом разделе.Класс
QuerySetимеет два публичных атрибута, которые вы можете использовать для интроспекции:-
ordered -
TrueеслиQuerySetотсортирован — т.е. имеет предложениеorder_by()или стандартную сортировку модели.Falseв противном случае.
-
db -
База данных, которая будет использоваться, если этот запрос будет выполнен сейчас.
Примечание
Параметр
queryдляQuerySetсуществует для того, чтобы специализированные подклассы запросов могли восстановить внутреннее состояние запроса. Значение параметра представляет собой неявное представление этого состояния запроса и не является частью публичного API. -
Методы, возвращающие новые QuerySet
Django предоставляет ряд методов уточнения QuerySet которые изменяют либо типы результатов, возвращаемых QuerySet, либо способ выполнения его запроса SQL.
filter()
-
filter(**kwargs)
Возвращает новый QuerySet, содержащий объекты, которые соответствуют заданным параметрам поиска.
Параметры поиска (**kwargs) должны быть в формате, описанном в Поисках по полям ниже. Несколько параметров объединяются через AND в базовом SQL-запросе.
Если вам нужно выполнить более сложные запросы (например, запросы с OR операторами), вы можете использовать Q objects.
exclude()
-
exclude(**kwargs)
Возвращает новый QuerySet, содержащий объекты, которые не соответствуют заданным параметрам поиска.
Параметры поиска (**kwargs) должны быть в формате, описанном в Поисках по полям ниже. Несколько параметров объединяются через AND в базовом SQL-запросе, и всё это заключено в NOT().
Этот пример исключает все записи, pub_date которых позже 2005-01-03 И headline которых «Hello»:
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')
В терминах SQL это эквивалентно:
SELECT ... WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')
Этот пример исключает все записи, pub_date которых позже 2005-01-03 ИЛИ заголовок которых «Hello»:
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline='Hello')
В терминах SQL это эквивалентно:
SELECT ... WHERE NOT pub_date > '2005-1-3' AND NOT headline = 'Hello'
Обратите внимание, что второй пример более ограничительный.
Если вам нужно выполнить более сложные запросы (например, запросы с OR операторами), вы можете использовать Q objects.
annotate()
-
annotate(*args, **kwargs)
Добавляет к каждому объекту в QuerySet заданный список выражений запроса. Выражение может быть простым значением, ссылкой на поле в модели (или любой связанной модели), или агрегированным выражением (средние значения, суммы и т. д.), которое вычислено по объектам, связанным с объектами в QuerySet.
Каждый аргумент для annotate() является аннотацией, которая будет добавлена к каждому объекту в возвращаемом QuerySet.
Функции агрегации, предоставляемые Django, описаны ниже в разделе Функции агрегации.
Аннотации, заданные с помощью ключевых аргументов, будут использовать ключевое слово в качестве псевдонима аннотации. Анонимные аргументы получат псевдоним, сгенерированный на основе имени функции агрегирования и поля модели, которое агрегируется. Только выражения агрегирования, ссылающиеся на одно поле, могут быть анонимными аргументами. Всё остальное должно быть ключевым аргументом.
Например, если вы работаете со списком блогов, вы можете захотеть определить, сколько записей было сделано в каждом блоге:
>>> from django.db.models import Count
>>> q = Blog.objects.annotate(Count('entry'))
# The name of the first blog
>>> q[0].name
'Blogasaurus'
# The number of entries on the first blog
>>> q[0].entry__count
42
Модель Blog не определяет атрибут entry__count сама по себе, но с помощью ключевого аргумента для указания функции агрегирования вы можете контролировать имя аннотации:
>>> q = Blog.objects.annotate(number_of_entries=Count('entry'))
# The number of entries on the first blog, using the name provided
>>> q[0].number_of_entries
42
Для более глубокого обсуждения агрегирования см. руководство по теме Агрегирование.
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. Это приводит к SELECT DISTINCT ON SQL-запросу. Вот в чём разница. При обычном distinct() вызове база данных сравнивает каждое поле в каждой строке для определения уникальных строк. При distinct() вызове с указанными именами полей база данных будет сравнивать только указанные имена полей.
Примечание
При указании имён полей вы обязательно должны указать order_by() в QuerySet, и поля в order_by() должны начинаться с полей в distinct(), в том же порядке.
Например, SELECT DISTINCT ON (a) возвращает первую строку для каждого значения в столбце a. Если вы не укажете порядок, вы получите произвольную строку.
Примеры (после первого будут работать только в PostgreSQL):
>>> Author.objects.distinct()
[...]
>>> Entry.objects.order_by('pub_date').distinct('pub_date')
[...]
>>> Entry.objects.order_by('blog').distinct('blog')
[...]
>>> Entry.objects.order_by('author', 'pub_date').distinct('author', 'pub_date')
[...]
>>> Entry.objects.order_by('blog__name', 'mod_date').distinct('blog__name', 'mod_date')
[...]
>>> Entry.objects.order_by('author', 'pub_date').distinct('author')
[...]
Примечание
Помните, что order_by() использует любой заданный по умолчанию порядок сортировки связанной модели. Вам может потребоваться явно отсортировать по связи _id или связанному полю, чтобы убедиться, что выражения DISTINCT ON совпадают с теми, что в начале ORDER BY условия. Например, если модель Blog определила ordering по name,
Entry.objects.order_by('blog').distinct('blog')
…это не сработает, потому что запрос будет отсортирован по blog__name, что не соответствует выражению DISTINCT ON. Вам нужно явно отсортировать по полю связи _id (blog_id в данном случае) или связанному полю (blog__pk) для обеспечения соответствия обоих выражений.
values()
-
values(*fields, **expressions)
Возвращает QuerySet, который возвращает словари, а не экземпляры моделей, когда используется как итерируемый объект.
Каждый из этих словарей представляет собой объект, где ключи соответствуют именам атрибутов объектов модели.
В этом примере сравниваются словари values() с обычными объектами модели:
# This list contains a Blog object.
>>> Blog.objects.filter(name__startswith='Beatles')
<QuerySet [<Blog: Beatles Blog>]>
# This list contains a dictionary.
>>> Blog.objects.filter(name__startswith='Beatles').values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
Метод values() принимает необязательные позиционные аргументы, *fields, которые указывают имена полей, до которых должен быть ограничен SELECT. Если вы укажете поля, каждый словарь будет содержать только ключи/значения полей, которые вы указали. Если вы не укажете поля, каждый словарь будет содержать ключ и значение для каждого поля в таблице базы данных.
Пример:
>>> Blog.objects.values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
>>> Blog.objects.values('id', 'name')
<QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>
Метод values() также принимает необязательные ключевые аргументы, **expressions, которые передаются в annotate():
>>> from django.db.models.functions import Lower
>>> Blog.objects.values(lower_name=Lower('name'))
<QuerySet [{'lower_name': 'beatles blog'}]>
Вы можете использовать встроенные и пользовательские функции сортировки. Например:
>>> from django.db.models import CharField
>>> from django.db.models.functions import Lower
>>> CharField.register_lookup(Lower)
>>> Blog.objects.values('name__lower')
<QuerySet [{'name__lower': 'beatles blog'}]>
Агрегация в values() условии применяется до других аргументов в том же values() условии. Если вам нужно сгруппировать по другому значению, добавьте его в предыдущее values() условие. Например:
>>> from django.db.models import Count
>>> Blog.objects.values('entry__authors', entries=Count('entry'))
<QuerySet [{'entry__authors': 1, 'entries': 20}, {'entry__authors': 1, 'entries': 13}]>
>>> Blog.objects.values('entry__authors').annotate(entries=Count('entry'))
<QuerySet [{'entry__authors': 1, 'entries': 33}]>
Несколько нюансов, которые стоит упомянуть:
-
Если у вас есть поле под названием
foo, которое являетсяForeignKey, обычный вызовvalues()вернёт ключ словаря под названиемfoo_id, так как это имя скрытого атрибута модели, хранящего фактическое значение (атрибутfooотносится к связанной модели). Когда вы вызываетеvalues()и передаёте имена полей, вы можете передать либоfoo, либоfoo_id, и получите то же самое (ключ словаря будет соответствовать имени поля, которое вы передали).Например:
>>> Entry.objects.values() <QuerySet [{'blog_id': 1, 'headline': 'First Entry', ...}, ...]> >>> Entry.objects.values('blog') <QuerySet [{'blog': 1}, ...]> >>> Entry.objects.values('blog_id') <QuerySet [{'blog_id': 1}, ...]> - При использовании
values()вместе сdistinct(), помните, что сортировка может повлиять на результаты. См. примечание вdistinct()для получения подробностей. - Если вы используете
values()условие после вызоваextra(), любые поля, определённые аргументомselectвextra(), должны быть явно включены в вызовvalues(). Любой вызовextra(), сделанный после вызоваvalues(), проигнорирует дополнительные выбранные поля. - Вызов
only()иdefer()послеvalues()не имеет смысла, поэтому это приведёт к ошибкеTypeError. -
Объединение преобразований и агрегаций требует использования двух вызовов
annotate(), явно или в качестве аргументов ключевых слов дляvalues(). Как и выше, если преобразование зарегистрировано для соответствующего типа поля, первый вызовannotate()может быть опущен, следовательно, следующие примеры эквивалентны:>>> from django.db.models import CharField, Count >>> from django.db.models.functions import Lower >>> CharField.register_lookup(Lower) >>> Blog.objects.values('entry__authors__name__lower').annotate(entries=Count('entry')) <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]> >>> Blog.objects.values( ... entry__authors__name__lower=Lower('entry__authors__name') ... ).annotate(entries=Count('entry')) <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]> >>> Blog.objects.annotate( ... entry__authors__name__lower=Lower('entry__authors__name') ... ).values('entry__authors__name__lower').annotate(entries=Count('entry')) <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]>
Это полезно, когда вы знаете, что вам понадобятся значения только из небольшого числа доступных полей, и вам не понадобится функциональность объекта модели. Эффективнее выбрать только необходимые поля.
Наконец, обратите внимание, что вы можете вызвать filter(), order_by(), и т. д. после вызова values(), что означает, что эти два вызова идентичны:
Blog.objects.values().order_by('id')
Blog.objects.order_by('id').values()
Авторы Django предпочитают сначала помещать все методы, влияющие на SQL, а затем (по желанию) любые методы, влияющие на вывод (например, values()). Но это не имеет значения. Вот ваш шанс проявить индивидуальность.
Вы также можете обращаться к полям связанных моделей с обратными связями через атрибуты OneToOneField, ForeignKey и ManyToManyField.
>>> Blog.objects.values('name', 'entry__headline')
<QuerySet [{'name': 'My blog', 'entry__headline': 'An entry'},
{'name': 'My blog', 'entry__headline': 'Another entry'}, ...]>
Предупреждение
Поскольку атрибуты ManyToManyField и обратные связи могут иметь несколько связанных строк, включение их может многократно увеличить размер набора результатов. Это особенно заметно, если вы включаете несколько таких полей в ваш values() запрос, в этом случае будут возвращены все возможные комбинации.
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,)]>
dates()
-
dates(field, kind, order='ASC')
Возвращает QuerySet, который вычисляет список объектов datetime.date, представляющих все доступные даты определенного типа в содержимом QuerySet.
field должно быть именем DateField вашей модели. kind должно быть либо "year", либо "month", либо "week", либо "day". Каждый объект datetime.date в списке результатов «обрезается» до заданного type.
-
"year"возвращает список всех уникальных значений года для поля. -
"month"возвращает список всех уникальных значений год/месяц для поля. -
"week"возвращает список всех уникальных значений год/неделя для поля. Все даты будут понедельниками. -
"day"возвращает список всех уникальных значений год/месяц/день для поля.
order, по умолчанию 'ASC', должно быть либо 'ASC', либо 'DESC'. Это определяет порядок результатов.
Примеры:
>>> Entry.objects.dates('pub_date', 'year')
[datetime.date(2005, 1, 1)]
>>> Entry.objects.dates('pub_date', 'month')
[datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)]
>>> Entry.objects.dates('pub_date', 'week')
[datetime.date(2005, 2, 14), datetime.date(2005, 3, 14)]
>>> Entry.objects.dates('pub_date', 'day')
[datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)]
>>> Entry.objects.dates('pub_date', 'day', order='DESC')
[datetime.date(2005, 3, 20), datetime.date(2005, 2, 20)]
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
[datetime.date(2005, 3, 20)]
datetimes()
-
datetimes(field_name, kind, order='ASC', tzinfo=None, is_dst=None)
Возвращает QuerySet, который вычисляет список объектов datetime.datetime, представляющих все доступные даты определенного типа в содержимом QuerySet.
field_name должно быть именем DateTimeField вашей модели.
kind должно быть либо "year", либо "month", либо "week", либо "day", либо "hour", либо "minute", либо "second". Каждый объект datetime.datetime в списке результатов «обрезается» до заданного type.
order, по умолчанию 'ASC', должно быть либо 'ASC', либо 'DESC'. Это определяет порядок результатов.
tzinfo определяет часовой пояс, в который преобразуются даты и время перед обрезкой. Действительно, данная дата и время имеют разные представления в зависимости от используемого часового пояса. Этот параметр должен быть объектом datetime.tzinfo. Если это None, Django использует текущий часовой пояс. Он не влияет, когда USE_TZ имеет значение False.
is_dst указывает, следует ли pytz интерпретировать несуществующие и неоднозначные даты и время в летнее время. По умолчанию (если is_dst=None), pytz генерирует исключение для таких дат и времени.
Был добавлен параметр is_dst.
Примечание
Эта функция выполняет преобразования часовых поясов непосредственно в базе данных. Вследствие этого ваша база данных должна уметь интерпретировать значение tzinfo.tzname(None).
- SQLite: нет требований. Преобразования выполняются в Python с помощью pytz (установлен при установке Django).
- PostgreSQL: нет требований (см. Часовые пояса).
- Oracle: нет требований (см. Выбор файла часового пояса).
- MySQL: загрузите таблицы часовых поясов с помощью mysql_tzinfo_to_sql.
none()
-
none()
Вызов none() создаст запрос, который никогда не вернет объекты, и при обращении к результатам запрос не будет выполнен. Запрос qs.none() является экземпляром EmptyQuerySet.
Примеры:
>>> Entry.objects.none() <QuerySet []> >>> from django.db.models.query import EmptyQuerySet >>> isinstance(Entry.objects.none(), EmptyQuerySet) True
all()
-
all()
Возвращает копию текущего QuerySet (или подкласса QuerySet). Это может быть полезно в ситуациях, когда вы хотите передать либо менеджер модели, либо QuerySet и выполнить дополнительную фильтрацию результата. После вызова all() для любого из объектов, вы обязательно получите QuerySet для работы.
Когда QuerySet оценивается, он обычно кэширует результаты. Если данные в базе данных могли измениться с момента оценки QuerySet, вы можете получить обновленные результаты для того же запроса, вызвав all() для ранее оцененного QuerySet.
union()
-
union(*other_qs, all=False)
Использует оператор SQL UNION для объединения результатов двух или более QuerySet.
>>> qs1.union(qs2, qs3)
Оператор UNION по умолчанию выбирает только уникальные значения. Чтобы разрешить дубликаты, используйте аргумент all=True.
union(), intersection(), и difference() возвращают экземпляры моделей типа первой QuerySet, даже если аргументы являются 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(), 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. Это позволяет ему предварительно загружать объекты многие-ко-многим и многие-ко-одному, что невозможно сделать с помощью select_related, в дополнение к внешним ключам и отношениям один-к-одному, поддерживаемым select_related. Он также поддерживает предварительную загрузку GenericRelation и GenericForeignKey, однако, он должен быть ограничен однородным набором результатов. Например, предварительная загрузка объектов, на которые ссылается GenericForeignKey , поддерживается только в том случае, если запрос ограничен одним ContentType.
Например, предположим, что у вас есть эти модели:
from django.db import models
class Topping(models.Model):
name = models.CharField(max_length=30)
class Pizza(models.Model):
name = models.CharField(max_length=50)
toppings = models.ManyToManyField(Topping)
def __str__(self):
return "%s (%s)" % (
self.name,
", ".join(topping.name for topping in self.toppings.all()),
)
и выполните:
>>> Pizza.objects.all() ["Hawaiian (ham, pineapple)", "Seafood (prawns, smoked salmon)"...
Проблема в том, что каждый раз, когда Pizza.__str__() запрашивает self.toppings.all(), ему нужно обращаться к базе данных, поэтому Pizza.objects.all() выполнит запрос к таблице Toppings для каждого элемента в пицце QuerySet.
Мы можем уменьшить до двух запросов, используя prefetch_related:
>>> Pizza.objects.all().prefetch_related('toppings')
Это подразумевает self.toppings.all() для каждого Pizza; теперь каждый раз, когда self.toppings.all() вызывается, вместо того, чтобы обращаться к базе данных за элементами, он найдёт их в кэше предварительной загрузки QuerySet , который был заполнен в одном запросе.
То есть, все соответствующие добавки были извлечены в одном запросе и использованы для создания QuerySets , имеющих предварительно заполненный кэш соответствующих результатов; эти QuerySets затем используются в вызовах self.toppings.all().
Дополнительные запросы в prefetch_related() выполняются после того, как QuerySet начал оцениваться и был выполнен основной запрос.
Если у вас есть итерируемый список экземпляров моделей, вы можете предварительно загрузить связанные атрибуты этих экземпляров, используя функцию prefetch_related_objects().
Обратите внимание, что кэш результатов основного запроса QuerySet и всех указанных связанных объектов будут полностью загружены в память. Это меняет типичное поведение QuerySets, которое обычно старается избегать загрузки всех объектов в память до тех пор, пока они не потребуются, даже после выполнения запроса в базе данных.
Примечание
Помните, что, как и всегда с QuerySets, любые последующие связанные методы, которые подразумевают другой запрос к базе данных, игнорируют предварительно кэшированные результаты и извлекают данные с помощью нового запроса к базе данных. Итак, если вы напишете следующее:
>>> pizzas = Pizza.objects.prefetch_related('toppings')
>>> [list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]
…тот факт, что pizza.toppings.all() был предварительно загружен, не поможет вам. prefetch_related('toppings') подразумевает pizza.toppings.all(), но pizza.toppings.filter() — это новый и другой запрос. Кэш предварительной загрузки здесь не поможет; на самом деле это ухудшает производительность, поскольку вы выполнили запрос к базе данных, который не использовали. Поэтому используйте эту функцию с осторожностью!
Кроме того, если вы вызываете методы изменения базы данных add(), remove(), clear() или set() на related managers, любой кэш предварительной загрузки для отношения будет очищен.
Также можно использовать обычный синтаксис соединения для работы со связанными полями связанных полей. Предположим, что у нас есть дополнительная модель к приведенному выше примеру:
class Restaurant(models.Model):
pizzas = models.ManyToManyField(Pizza, related_name='restaurants')
best_pizza = models.ForeignKey(Pizza, related_name='championed_by', on_delete=models.CASCADE)
Следующие варианты допустимы:
>>> Restaurant.objects.prefetch_related('pizzas__toppings')
Это предварительно загрузит все пиццы, принадлежащие ресторанам, и все добавки, принадлежащие этим пиццам. Это приведёт к общему количеству 3 запросов к базе данных — один для ресторанов, один для пицц и один для добавок.
>>> Restaurant.objects.prefetch_related('best_pizza__toppings')
Это извлечёт лучшую пиццу и все добавки к лучшей пицце для каждого ресторана. Это будет выполнено в 3 запросах к базе данных — один для ресторанов, один для «лучших пицц» и один для добавок.
Отношение best_pizza также можно извлечь, используя select_related , чтобы сократить количество запросов до 2:
>>> Restaurant.objects.select_related('best_pizza').prefetch_related('best_pizza__toppings')
Поскольку предварительная загрузка выполняется после основного запроса (который включает объединения, необходимые для select_related), он может определить, что объекты best_pizza уже были извлечены, и пропустит их повторное извлечение.
Цепочки вызовов prefetch_related будут накапливать запросы, которые предварительно загружаются. Чтобы очистить любое поведение prefetch_related, передайте None в качестве параметра:
>>> non_prefetched = qs.prefetch_related(None)
Одно отличие при использовании prefetch_related заключается в том, что объекты, созданные запросом, могут быть совмещены между различными объектами, к которым они относятся, т. е. один экземпляр Python-модели может появляться более чем в одном месте в дереве возвращаемых объектов. Обычно это происходит с отношениями внешнего ключа. Обычно это поведение не станет проблемой и, фактически, сэкономит память и время ЦП.
Хотя prefetch_related поддерживает предварительную загрузку GenericForeignKey отношений, количество запросов будет зависеть от данных. Поскольку GenericForeignKey может ссылаться на данные в нескольких таблицах, требуется по одному запросу на каждую таблицу, а не один запрос для всех элементов. Может быть дополнительный запрос к таблице ContentType , если соответствующие строки ещё не извлечены.
prefetch_related в большинстве случаев будет реализовано с помощью SQL-запроса, использующего оператор «IN». Это означает, что для большого QuerySet может быть сгенерирована большая строка «IN», которая в зависимости от базы данных может иметь собственные проблемы производительности при анализе или выполнении SQL-запроса. Профилируйте для вашего случая!
Обратите внимание, что если вы используете iterator() для выполнения запроса, вызовы prefetch_related() будут проигнорированы, так как эти две оптимизации не имеют смысла вместе.
Вы можете использовать объект Prefetch для дальнейшего управления операцией предварительной загрузки.
В самом простом виде Prefetch эквивалентен традиционным поисковым запросам на основе строк:
>>> from django.db.models import Prefetch
>>> Restaurant.objects.prefetch_related(Prefetch('pizzas__toppings'))
Вы можете предоставить пользовательский набор результатов с необязательным аргументом queryset. Это можно использовать для изменения стандартного порядка сортировки набора результатов:
>>> Restaurant.objects.prefetch_related(
... Prefetch('pizzas__toppings', queryset=Toppings.objects.order_by('name')))
Или для вызова select_related() применительно к ситуации, чтобы ещё больше сократить количество запросов:
>>> Pizza.objects.prefetch_related(
... Prefetch('restaurants', queryset=Restaurant.objects.select_related('best_pizza')))
Вы также можете назначить результат предварительной загрузки пользовательскому атрибуту с необязательным аргументом QuerySet. Результат будет сохранён непосредственно в списке.
Это позволяет предварительно загружать одно и то же отношение несколько раз с разным 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;
Обратите внимание, что скобки, необходимые большинству движков баз данных вокруг подзапросов, не нужны в
selectстроках Django. Также обратите внимание, что некоторые движки баз данных, такие как некоторые версии MySQL, не поддерживают подзапросы.В некоторых редких случаях вы можете передавать параметры в фрагменты SQL в
extra(select=...). Для этого используйте параметрselect_params.Это будет работать, например:
Blog.objects.extra( select={'a': '%s', 'b': '%s'}, select_params=('one', 'two'), )Если вам нужно использовать литеральную
%sвнутри вашей строки запроса, используйте последовательность%%s. -
where/tablesВы можете определить явные SQL
WHEREстроки — возможно, для выполнения неявных объединений — используяwhere. Вы можете вручную добавить таблицы вFROMSQL строку, используя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")
Набор запросов с отложенными полями по-прежнему будет возвращать экземпляры моделей. Каждое отложенное поле будет извлекаться из базы данных, если вы обратитесь к этому полю (по одному за раз, а не ко всем отложенным полям сразу).
Вы можете сделать несколько вызовов defer(). Каждый вызов добавляет новые поля в отложенный набор:
# Defers both the body and headline fields.
Entry.objects.defer("body").filter(rating=5).defer("headline")
Порядок добавления полей в отложенный набор не имеет значения. Вызов defer() с именем поля, которое уже было отложено, безопасен (поле по-прежнему будет отложено).
Вы можете отложить загрузку полей в связанных моделях (если связанные модели загружаются через select_related()) с использованием стандартной нотации с двойным подчеркиванием для разделения связанных полей:
Blog.objects.select_related().defer("entry__headline", "entry__body")
Если вы хотите очистить набор отложенных полей, передайте None в качестве параметра к defer():
# Load all fields immediately. my_queryset.defer(None)
Некоторые поля в модели не будут отложены, даже если вы их запросите. Вы никогда не можете отложить загрузку первичного ключа. Если вы используете select_related() для извлечения связанных моделей, вы не должны откладывать загрузку поля, которое соединяет первичную модель со связанной, так как это приведет к ошибке.
Примечание
Метод defer() (и его аналог only(), ниже) предназначены только для продвинутых случаев использования. Они обеспечивают оптимизацию, когда вы тщательно проанализировали свои запросы и точно знаете, какая информация вам нужна, и измерили, что разница между возвращением необходимых полей и полного набора полей модели будет значительной.
Даже если вы считаете, что вы находитесь в ситуации продвинутого использования, используйте defer() только тогда, когда во время загрузки набора запросов вы не можете определить, понадобятся ли вам дополнительные поля или нет. Если вы часто загружаете и используете определенный подмножество данных, наилучшим решением будет нормализация ваших моделей и размещение не загруженных данных в отдельной модели (и таблице базы данных). Если столбцы должны оставаться в одной таблице по какой-либо причине, создайте модель с Meta.managed = False (см. документацию managed attribute), содержащую только поля, которые вам обычно нужно загрузить и использовать там, где вы могли бы вызвать defer(). Это делает ваш код более понятным для читателя, немного быстрее и потребляет немного меньше памяти в процессе Python.
Например, обе эти модели используют одну и ту же базу данных:
class CommonlyUsedModel(models.Model):
f1 = models.CharField(max_length=10)
class Meta:
managed = False
db_table = 'app_largetable'
class ManagedModel(models.Model):
f1 = models.CharField(max_length=10)
f2 = models.CharField(max_length=10)
class Meta:
db_table = 'app_largetable'
# Two equivalent QuerySets:
CommonlyUsedModel.objects.all()
ManagedModel.objects.all().defer('f2')
Если во вспомогательной модели нужно дублировать много полей, лучше всего создать абстрактную модель с общими полями, а затем сделать так, чтобы вспомогательная и управляемая модели наследовали от абстрактной модели.
Примечание
При вызове save() для экземпляров с отложенными полями, будут сохранены только загруженные поля. Смотрите save() для получения более подробной информации.
only()
-
only(*fields)
Метод only() в большей степени является противоположностью defer(). Вы вызываете его с полями, которые не должны быть отложены при получении модели. Если у вас есть модель, где почти все поля нужно отложить, использование only() для указания дополнительного набора полей может привести к более простому коду.
Предположим, у вас есть модель с полями name, age и biography. Два следующих набора запросов одинаковы с точки зрения отложенных полей:
Person.objects.defer("age", "biography")
Person.objects.only("name")
Всякий раз, когда вы вызываете only() он заменяет набор полей, которые нужно загрузить немедленно. Название метода мнемоническое: только эти поля загружаются немедленно; остальные откладываются. Таким образом, последующие вызовы only() приводят к тому, что только последние поля учитываются:
# This will defer all fields except the headline.
Entry.objects.only("body", "rating").only("headline")
Поскольку defer() действует поэтапно (добавляя поля в список отложенных), вы можете комбинировать вызовы only() и defer() и всё будет работать логично:
# Final result is that everything except "headline" is deferred.
Entry.objects.only("headline", "body").defer("body")
# Final result loads headline and body immediately (only() replaces any
# existing set of fields).
Entry.objects.defer("body").only("headline", "body")
Все предупреждения в примечании к документации для defer() также относятся к only(). Используйте его осторожно и только после исчерпания других вариантов.
Использование only() и пропуск поля, запрошенного с помощью select_related(), является ошибкой.
Примечание
При вызове save() для экземпляров с отложенными полями, будут сохранены только загруженные поля. Смотрите save() для получения более подробной информации.
using()
-
using(alias)
Этот метод предназначен для управления базой данных, по отношению к которой будет оцениваться QuerySet, если вы используете более одной базы данных. Единственным аргументом этого метода является псевдоним базы данных, как определено в DATABASES.
Например:
# queries the database with the 'default' alias.
>>> Entry.objects.all()
# queries the database with the 'backup' alias
>>> Entry.objects.using('backup')
select_for_update()
-
select_for_update(nowait=False, skip_locked=False, of=())
Возвращает набор запросов, который заблокирует строки до конца транзакции, генерируя SQL-запрос SELECT ... FOR UPDATE на поддерживаемых базах данных.
Например:
from django.db import transaction
entries = Entry.objects.select_for_update().filter(author=request.user)
with transaction.atomic():
for entry in entries:
...
Когда набор запросов оценивается (for entry in entries в этом случае), все соответствующие записи будут заблокированы до конца блока транзакции, что означает, что другие транзакции не смогут изменить или получить блокировки на них.
Обычно, если другая транзакция уже получила блокировку на одной из выбранных строк, запрос будет блокироваться до тех пор, пока блокировка не будет освобождена. Если это не то поведение, которое вам нужно, вызовите select_for_update(nowait=True). Это сделает вызов неблокирующим. Если блокировка конфликтует с другой транзакцией, DatabaseError будет поднята при оценке набора запросов. Вы также можете игнорировать заблокированные строки, используя select_for_update(skip_locked=True) вместо этого. nowait и skip_locked взаимоисключают и попытка вызвать select_for_update() с обоими параметрами приведет к ValueError.
По умолчанию, select_for_update() блокирует все строки, выбранные запросом. Например, строки связанных объектов, указанные в select_related(), блокируются дополнительно к строкам модели набора запросов. Если этого не требуется, укажите связанные объекты, которые вы хотите заблокировать в select_for_update(of=(...)) с помощью того же синтаксиса полей, что и в select_related(). Используйте значение 'self' для ссылки на модель набора запросов.
Блокировка родительских моделей в select_for_update(of=(...))
Если вы хотите заблокировать родительские модели при использовании наследования от нескольких таблиц, вы должны указать поля связи родителя (по умолчанию <parent_model_name>_ptr) в аргументе of. Например:
Restaurant.objects.select_for_update(of=('self', 'place_ptr'))
Вы не можете использовать select_for_update() для nullable отношений:
>>> Person.objects.select_related('hometown').select_for_update()
Traceback (most recent call last):
...
django.db.utils.NotSupportedError: FOR UPDATE cannot be applied to the nullable side of an outer join
Чтобы избежать этого ограничения, вы можете исключить нулевые объекты, если вам они не нужны:
>>> Person.objects.select_related('hometown').select_for_update().exclude(hometown=None)
<QuerySet [<Person: ...)>, ...]>
В настоящее время postgresql, oracle, и mysql бэкенды баз данных поддерживают select_for_update(). Однако MariaDB 10.3+ поддерживает только аргумент nowait, а MySQL 8.0.1+ поддерживает аргументы nowait и skip_locked. MySQL и MariaDB не поддерживают аргумент of.
Передача nowait=True, skip_locked=True, или of в select_for_update() с использованием бэкендов баз данных, которые не поддерживают эти параметры, таких как MySQL, вызывает NotSupportedError. Это предотвращает непредвиденное блокирование кода.
Оценивание набора запросов с select_for_update() в режиме autocommit на бэкендах, которые поддерживают SELECT ... FOR UPDATE — это ошибка TransactionManagementError, потому что в этом случае строки не блокируются. Если это разрешено, это способствовало бы повреждению данных и могло бы легко быть вызвано кодом, который ожидает выполнения в транзакции за пределами транзакции.
Использование select_for_update() на бэкендах, которые не поддерживают SELECT ... FOR UPDATE (например, SQLite), не повлияет. SELECT ... FOR UPDATE не будет добавлено в запрос, и ошибка не возникает, если select_for_update() используется в режиме autocommit.
Предупреждение
Хотя select_for_update() обычно завершается ошибкой в режиме autocommit, так как TestCase автоматически оборачивает каждый тест в транзакцию, вызов select_for_update() в TestCase даже за пределами блока atomic() пройдёт (возможно, неожиданно) без подъёма TransactionManagementError. Для надлежащего тестирования select_for_update() следует использовать TransactionTestCase.
Определённые выражения могут быть не поддерживаемы
PostgreSQL не поддерживает select_for_update() с Window выражениями.
raw()
-
raw(raw_query, params=None, translations=None)
Принимает сырой SQL-запрос, выполняет его и возвращает экземпляр django.db.models.query.RawQuerySet. Этот экземпляр RawQuerySet можно перебирать так же, как и обычный QuerySet, чтобы получить экземпляры объектов.
См. Выполнение сырых SQL-запросов для получения дополнительной информации.
Предупреждение
raw() всегда запускает новый запрос и не учитывает предыдущие фильтры. Поэтому его обычно следует вызывать из Manager или из нового экземпляра QuerySet.
Операторы, возвращающие новые QuerySet
Объединённые наборы запросов должны использовать одну и ту же модель.
И (&)
Объединяет два QuerySet с помощью SQL-оператора AND.
Следующее эквивалентно:
Model.objects.filter(x=1) & Model.objects.filter(y=2) Model.objects.filter(x=1, y=2) from django.db.models import Q Model.objects.filter(Q(x=1) & Q(y=2))
Эквивалент в SQL:
SELECT ... WHERE x=1 AND y=2
ИЛИ (|)
Объединяет два 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
Методы, которые не возвращают QuerySet
Следующие QuerySet методы оценивают QuerySet и возвращают что-либо кроме QuerySet.
Эти методы не используют кэш (см. Кэширование и наборы запросов). Вместо этого они каждый раз обращаются к базе данных.
get()
-
get(**kwargs)
Возвращает объект, соответствующий заданным параметрам поиска, которые должны быть в формате, описанном в Поиска по полям. Вы должны использовать поиски, которые гарантируют уникальность, такие как первичный ключ или поля в уникальном ограничении. Например:
Entry.objects.get(id=1) Entry.objects.get(blog=blog, entry_number=1)
Если вы ожидаете, что набор запросов уже вернёт одну строку, вы можете использовать get() без аргументов, чтобы вернуть объект для этой строки:
Entry.objects.filter(pk=1).get()
Если get() не находит ни одного объекта, он поднимает исключение Model.DoesNotExist:
Entry.objects.get(id=-999) # raises Entry.DoesNotExist
Если get() находит более одного объекта, он поднимает исключение Model.MultipleObjectsReturned:
Entry.objects.get(name='A Duplicated Name') # raises Entry.MultipleObjectsReturned
Оба этих класса исключений являются атрибутами класса модели и специфичны для этой модели. Если вы хотите обрабатывать такие исключения от нескольких get() вызовов для разных моделей, вы можете использовать их обобщённые базовые классы. Например, вы можете использовать django.core.exceptions.ObjectDoesNotExist для обработки исключений DoesNotExist от нескольких моделей:
from django.core.exceptions import ObjectDoesNotExist
try:
blog = Blog.objects.get(id=1)
entry = Entry.objects.get(blog=blog, entry_number=1)
except ObjectDoesNotExist:
print("Either the blog or entry doesn't exist.")
create()
-
create(**kwargs)
Удобный метод для создания объекта и сохранения его в одном шаге. Таким образом:
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
и:
p = Person(first_name="Bruce", last_name="Springsteen") p.save(force_insert=True)
эквивалентны.
Параметр force_insert документирован в другом месте, но всё, что он означает, — это то, что новый объект всегда будет создан. Обычно вам об этом беспокоиться не нужно. Однако, если ваша модель содержит ручное значение первичного ключа, которое вы задаёте, и если это значение уже существует в базе данных, вызов create() завершится ошибкой IntegrityError, так как первичные ключи должны быть уникальными. Будьте готовы обрабатывать исключение, если вы используете ручные первичные ключи.
get_or_create()
-
get_or_create(defaults=None, **kwargs)
Удобный метод для поиска объекта с заданными kwargs (может быть пустым, если ваша модель имеет значения по умолчанию для всех полей), создавая его при необходимости.
Возвращает кортеж (object, created), где object — полученный или созданный объект, а created — булево значение, указывающее, был ли создан новый объект.
Это предназначено для предотвращения создания дублирующих объектов при одновременных запросах и в качестве сокращения для повторяющегося кода. Например:
try:
obj = Person.objects.get(first_name='John', last_name='Lennon')
except Person.DoesNotExist:
obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
obj.save()
Здесь, при одновременных запросах, могут быть предприняты несколько попыток сохранить Person с одними и теми же параметрами. Для предотвращения этой гонки условий вышеприведённый пример можно переписать, используя get_or_create() следующим образом:
obj, created = Person.objects.get_or_create(
first_name='John',
last_name='Lennon',
defaults={'birthday': date(1940, 10, 9)},
)
Любые ключевые аргументы, переданные в get_or_create() — кроме необязательного аргумента под названием defaults — будут использованы в вызове get(). Если объект найден, get_or_create() возвращает кортеж этого объекта и False.
Предупреждение
Этот метод является атомарным, предполагая, что база данных обеспечивает уникальность ключевых аргументов (см. unique или unique_together). Если поля, используемые в ключевых аргументах, не имеют ограничения на уникальность, одновременные вызовы этого метода могут привести к вставке нескольких строк с одинаковыми параметрами.
Вы можете указать более сложные условия для полученного объекта, объединяя get_or_create() с filter() и используя Q objects. Например, для получения Роберта или Боба Марли, если один из них существует, и создания последнего в противном случае:
from django.db.models import Q
obj, created = Person.objects.filter(
Q(first_name='Bob') | Q(first_name='Robert'),
).get_or_create(last_name='Marley', defaults={'first_name': 'Bob'})
Если найдено несколько объектов, get_or_create() поднимает MultipleObjectsReturned. Если объект не найден, get_or_create() создаст и сохранит новый объект, вернув кортеж нового объекта и True. Новый объект будет создан примерно по этому алгоритму:
params = {k: v for k, v in kwargs.items() if '__' not in k}
params.update({k: v() if callable(v) else v for k, v in defaults.items()})
obj = self.model(**params)
obj.save()
Другими словами, начать с любого не-'defaults' ключевого аргумента, который не содержит двойного нижнего подчёркивания (что указывает на неточные поиск). Затем добавить содержимое defaults, перезаписывая любые ключи при необходимости, и использовать результат в качестве ключевых аргументов для класса модели. Если в defaults есть какие-либо вызываемые объекты, оценить их. Как указано выше, это упрощение используемого алгоритма, но оно содержит все важные детали. Внутренняя реализация имеет больше проверок ошибок, чем это, и обрабатывает некоторые дополнительные граничные условия; если вы заинтересованы, прочитайте код.
Если у вас есть поле под названием defaults и вы хотите использовать его как точное значение поиска в get_or_create(), используйте 'defaults__exact', как показано ниже:
Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})
Метод get_or_create() имеет аналогичное поведение ошибок по отношению к create(), когда вы используете вручную заданные первичные ключи. Если необходимо создать объект, а ключ уже существует в базе данных, будет поднято исключение IntegrityError.
Наконец, несколько слов об использовании get_or_create() в представлениях Django. Пожалуйста, убедитесь, что вы используете его только в запросах POST, если у вас нет веской причины для этого. Запросы GET не должны оказывать никакого влияния на данные. Вместо этого используйте POST всякий раз, когда запрос на страницу имеет побочный эффект на ваши данные. Для более подробной информации см. Безопасные методы в спецификации HTTP.
Предупреждение
Вы можете использовать get_or_create() через атрибуты ManyToManyField и обратные связи. В этом случае вы ограничите запросы в контексте этой связи. Это может привести к проблемам целостности, если вы не используете его последовательно.
Рассмотрим следующие модели:
class Chapter(models.Model):
title = models.CharField(max_length=255, unique=True)
class Book(models.Model):
title = models.CharField(max_length=256)
chapters = models.ManyToManyField(Chapter)
Вы можете использовать get_or_create() через поле глав Book, но это происходит только в контексте этой книги:
>>> book = Book.objects.create(title="Ulysses") >>> book.chapters.get_or_create(title="Telemachus") (<Chapter: Telemachus>, True) >>> book.chapters.get_or_create(title="Telemachus") (<Chapter: Telemachus>, False) >>> Chapter.objects.create(title="Chapter 1") <Chapter: Chapter 1> >>> book.chapters.get_or_create(title="Chapter 1") # Raises IntegrityError
Это происходит потому, что он пытается получить или создать «Главу 1» через книгу «Улисс», но не может сделать ни того, ни другого: связь не может получить эту главу, потому что она не связана с этой книгой, но не может её создать и потому, что поле title должно быть уникальным.
update_or_create()
-
update_or_create(defaults=None, **kwargs)
Удобный метод для обновления объекта с заданными kwargs, создавая новый, если необходимо. defaults — это словарь пар (поле, значение), используемый для обновления объекта. Значения в defaults могут быть вызовами функций.
Возвращает кортеж (object, created), где object — это созданный или обновленный объект, а created — булево значение, указывающее, был ли создан новый объект.
Метод update_or_create пытается получить объект из базы данных на основе заданных kwargs. Если совпадение найдено, он обновляет поля, переданные в defaults словарь.
Это предназначено как сокращение для шаблонного кода. Например:
defaults = {'first_name': 'Bob'}
try:
obj = Person.objects.get(first_name='John', last_name='Lennon')
for key, value in defaults.items():
setattr(obj, key, value)
obj.save()
except Person.DoesNotExist:
new_values = {'first_name': 'John', 'last_name': 'Lennon'}
new_values.update(defaults)
obj = Person(**new_values)
obj.save()
Этот шаблон становится довольно громоздким по мере увеличения количества полей в модели. Приведенный выше пример можно переписать, используя update_or_create() следующим образом:
obj, created = Person.objects.update_or_create(
first_name='John', last_name='Lennon',
defaults={'first_name': 'Bob'},
)
Для подробного описания того, как разрешаются имена, переданные в kwargs, см. get_or_create().
Как описано выше в get_or_create(), этот метод подвержен проблеме гонки, которая может привести к одновременной вставке нескольких строк, если уникальность не гарантируется на уровне базы данных.
Как и get_or_create() и create(), если вы используете вручную заданные первичные ключи и объект должен быть создан, но ключ уже существует в базе данных, генерируется исключение IntegrityError.
bulk_create()
-
bulk_create(objs, batch_size=None, ignore_conflicts=False)
Этот метод вставляет предоставленный список объектов в базу данных эффективным способом (обычно только 1 запрос, независимо от количества объектов):
>>> Entry.objects.bulk_create([ ... Entry(headline='This is a test'), ... Entry(headline='This is only a test'), ... ])
Однако это имеет ряд ограничений:
- Метод
save()модели не будет вызываться, и сигналыpre_saveиpost_saveне будут отправляться. - Он не работает с дочерними моделями в сценарии наследования с несколькими таблицами.
- Если первичный ключ модели — это
AutoField, атрибут первичного ключа можно получить только в некоторых базах данных (в настоящее время PostgreSQL и MariaDB 10.5+). В других базах данных он не будет установлен. - Он не работает со множественными связями.
-
Он преобразует
objsв список, который полностью вычисляетobjs, если это генератор. Преобразование позволяет проверить все объекты, чтобы все объекты с вручную заданным первичным ключом можно было вставить первыми. Если вы хотите вставлять объекты партиями, не вычисляя весь генератор сразу, вы можете использовать этот метод, если у объектов нет вручную установленных первичных ключей:from itertools import islice batch_size = 100 objs = (Entry(headline='Test %s' % i) for i in range(1000)) while True: batch = list(islice(objs, batch_size)) if not batch: break Entry.objects.bulk_create(batch, batch_size)
Параметр batch_size управляет количеством объектов, создаваемых в одном запросе. По умолчанию все объекты создаются в одной партии, за исключением SQLite, где по умолчанию используется не более 999 переменных на запрос.
В базах данных, которые это поддерживают (все кроме Oracle), установка параметра ignore_conflicts в True указывает базе данных игнорировать ошибки вставки строк, которые не соответствуют ограничениям, таким как дубликаты уникальных значений. Включение этого параметра отключает установку первичного ключа для каждого экземпляра модели (если база данных это обычно поддерживает).
Предупреждение
В MySQL и MariaDB установка параметра ignore_conflicts в True превращает определенные типы ошибок, отличные от ошибки дублирования ключа, в предупреждения. Даже в режиме строгого соответствия. Например: неверные значения или нарушения обязательности значений. См. документацию MySQL по этому вопросу и документацию MariaDB по этому вопросу.
Возвращает objs как список, в том же порядке, что и входные данные.
Добавлена поддержка получения атрибутов первичного ключа в MariaDB 10.5+.
bulk_update()
-
bulk_update(objs, fields, batch_size=None)
Этот метод эффективно обновляет указанные поля для предоставленных экземпляров модели, обычно с одним запросом:
>>> objs = [ ... Entry.objects.create(headline='Entry 1'), ... Entry.objects.create(headline='Entry 2'), ... ] >>> objs[0].headline = 'This is entry 1' >>> objs[1].headline = 'This is entry 2' >>> Entry.objects.bulk_update(objs, ['headline'])
QuerySet.update() используется для сохранения изменений, поэтому это более эффективно, чем итерация по списку моделей и вызов save() для каждой из них, но у него есть несколько ограничений:
- Вы не можете обновить первичный ключ модели.
- Метод
save()каждой модели не вызывается, и сигналыpre_saveиpost_saveне отправляются. - Если вы обновляете большое количество столбцов в большом количестве строк, генерируемый SQL-запрос может быть очень большим. Избегайте этого, указав подходящее значение для
batch_size. - Обновление полей, определённых в родительских классах наследования с несколькими таблицами, потребует дополнительного запроса на каждый родительский класс.
- Если
objsсодержит дубликаты, обновляется только первый.
Параметр batch_size управляет количеством объектов, сохраняемых в одном запросе. По умолчанию все объекты обновляются в одной партии, за исключением SQLite и Oracle, где есть ограничения на количество переменных в запросе.
count()
-
count()
Возвращает целое число, представляющее количество объектов в базе данных, соответствующих QuerySet.
Пример:
# Returns the total number of entries in the database. Entry.objects.count() # Returns the number of entries whose headline contains 'Lennon' Entry.objects.filter(headline__contains='Lennon').count()
Вызов count() выполняет запрос SELECT COUNT(*) за кулисами, поэтому вы всегда должны использовать count() вместо загрузки всех записей в объекты Python и вызова len() на результате (если вам не нужно загружать объекты в память, в этом случае len() будет быстрее).
Обратите внимание, что если вам нужно количество элементов в QuerySet и вы также получаете экземпляры моделей из него (например, итерацией по нему), вероятно, более эффективно использовать len(queryset), что не вызовет дополнительного запроса к базе данных, в отличие от count().
in_bulk()
-
in_bulk(id_list=None, field_name='pk')
Принимает список значений поля (id_list) и field_name для этих значений и возвращает словарь, сопоставляющий каждое значение с экземпляром объекта с заданным значением поля. Если id_list не указано, возвращаются все объекты в наборе запросов. 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>}
Если вы передадите in_bulk() пустой список, вы получите пустой словарь.
iterator()
-
iterator(chunk_size=2000)
Вычисляет QuerySet (выполняя запрос) и возвращает итератор (см. PEP 234) по результатам. Итератор QuerySet обычно кэширует свои результаты, чтобы повторные вычисления не приводили к дополнительным запросам. В отличие от этого, iterator() читает результаты непосредственно, без кэширования на уровне QuerySet (внутренне, итератор по умолчанию вызывает iterator() и кэширует возвращаемое значение). Для QuerySet , возвращающего большое количество объектов, которые вам нужно получить только один раз, это может привести к лучшей производительности и значительному снижению потребления памяти.
Обратите внимание, что использование iterator() на QuerySet , который уже был вычислен, заставит его вычислиться снова, повторив запрос.
Также, использование iterator() игнорирует предыдущие вызовы prefetch_related(), так как эти два оптимизации не имеют смысла вместе.
В зависимости от бэкенда базы данных, результаты запроса будут загружены все сразу или переданы из базы данных с помощью серверных курсоров.
END_OF_DOCUMENT_MARKER ```С серверными курсорами
Oracle и PostgreSQL используют серверные курсоры для потоковой передачи результатов из базы данных без загрузки всего набора результатов в память.
Драйвер базы данных Oracle всегда использует серверные курсоры.
С серверными курсорами параметр chunk_size задаёт количество результатов для кэширования на уровне драйвера базы данных. Получение больших блоков уменьшает количество обменов между драйвером базы данных и базой данных, но за счёт памяти.
В PostgreSQL серверные курсоры будут использоваться только тогда, когда значение параметра DISABLE_SERVER_SIDE_CURSORS равно False. Прочитайте Пулы транзакций и серверные курсоры, если вы используете пул соединений, настроенный в режиме пулов транзакций. При отключении серверных курсоров поведение будет таким же, как у баз данных, которые не поддерживают серверные курсоры.
Без серверных курсоров
MySQL не поддерживает потоковую передачу результатов, поэтому драйвер базы данных Python загружает весь набор результатов в память. Затем набор результатов преобразуется в объекты строк Python адаптером базы данных с помощью метода fetchmany() , определённого в PEP 249.
SQLite может получать результаты партиями, используя fetchmany(), но поскольку SQLite не обеспечивает изоляцию между запросами в рамках одного соединения, будьте осторожны при записи в таблицу, по которой происходит итерирование. См. Изоляция при использовании QuerySet.iterator() для получения дополнительной информации.
Параметр chunk_size контролирует размер партий, которые Django получает от драйвера базы данных. Большие партии уменьшают нагрузку на общение с драйвером базы данных за счёт незначительного увеличения потребления памяти.
Значение по умолчанию для chunk_size, 2000, взято из расчёта на почтовом списке psycopg:
latest()
-
latest(*fields)
Возвращает самый свежий объект в таблице, основанный на указанном(ых) поле(ях).
Этот пример возвращает самую последнюю Entry в таблице по полю pub_date.
Entry.objects.latest('pub_date')
Вы также можете выбрать самый свежий по нескольким полям. Например, чтобы выбрать Entry с самой ранней expire_date, когда у двух записей одинаковое значение pub_date:
Entry.objects.latest('pub_date', '-expire_date')
Отрицательный знак в '-expire_date' означает сортировку expire_date в понижающем порядке. Поскольку latest() получает последний результат, выбирается Entry с самым ранним expire_date.
Если в Meta вашей модели указано get_latest_by, вы можете опустить любые аргументы к earliest() или latest() . Поля, указанные в get_latest_by, будут использоваться по умолчанию.
Как и get(), earliest() и latest() вызывают DoesNotExist, если объект с заданными параметрами отсутствует.
Обратите внимание, что earliest() и latest() существуют только для удобства и читаемости.
earliest() и latest() могут возвращать экземпляры с датами NULL.
Так как сортировка делегирована базе данных, результаты по полям, допускающим значения NULL, могут быть отсортированы по-разному, если вы используете разные базы данных. Например, PostgreSQL и MySQL сортируют значения NULL так, как будто они больше, чем ненулевые значения, в то время как SQLite делает наоборот.
Вы можете захотеть отфильтровать значения NULL:
Entry.objects.filter(pub_date__isnull=False).latest('pub_date')
earliest()
-
earliest(*fields)
В остальном работает как latest(), за исключением изменения направления.
first()
-
first()
Возвращает первый объект, соответствующий набору запросов, или None , если такого объекта нет. Если у набора запросов нет определённого порядка сортировки, то набор запросов автоматически сортируется по первичному ключу. Это может повлиять на результаты агрегации, как описано в Взаимодействие с порядком сортировки по умолчанию или order_by().
Пример:
p = Article.objects.order_by('title', 'pub_date').first()
Обратите внимание, что first() — это вспомогательный метод; следующий фрагмент кода эквивалентен вышеприведённому примеру:
try:
p = Article.objects.order_by('title', 'pub_date')[0]
except IndexError:
p = None
last()
-
last()
Работает как first(), но возвращает последний объект в наборе запросов.
aggregate()
-
aggregate(*args, **kwargs)
Возвращает словарь агрегированных значений (средних, сумм и т. д.), вычисленных по QuerySet. Каждый аргумент в aggregate() определяет значение, которое будет включено в возвращаемый словарь.
Функции агрегации, которые предоставляет Django, описаны в разделе Функции агрегации ниже. Поскольку агрегаты также являются выражениями запроса, вы можете комбинировать агрегаты с другими агрегатами или значениями для создания сложных агрегатов.
Агрегаты, указанные с помощью ключевых аргументов, будут использовать ключевое слово в качестве имени для аннотации. Анонимные аргументы будут иметь имя, сгенерированное на основе имени функции агрегации и поля модели, которое агрегируется. Сложные агрегаты не могут использовать анонимные аргументы и должны указывать ключевой аргумент в качестве псевдонима.
Например, при работе со статьями блога вы можете захотеть узнать количество авторов, которые внесли вклад в статьи блога:
>>> from django.db.models import Count
>>> q = Blog.objects.aggregate(Count('entry'))
{'entry__count': 16}
Используя ключевой аргумент для указания функции агрегации, вы можете контролировать имя значения агрегации, которое возвращается:
>>> q = Blog.objects.aggregate(number_of_entries=Count('entry'))
{'number_of_entries': 16}
Для углублённого обсуждения агрегации см. руководство по агрегации.
exists()
-
exists()
Возвращает True , если набор запросов QuerySet содержит какие-либо результаты, и False в противном случае. Это пытается выполнить запрос наиболее простым и быстрым способом, но оно *действительно* выполняет почти такой же запрос, как обычный запрос набора запросов QuerySet.
exists() полезно для поиска, связанного как с принадлежностью объекта к набору запросов QuerySet, так и с существованием каких-либо объектов в наборе запросов QuerySet, особенно в контексте большого набора запросов QuerySet.
Самый эффективный способ определить, является ли модель с уникальным полем (например, primary_key) членом набора запросов QuerySet:
entry = Entry.objects.get(pk=123)
if some_queryset.filter(pk=entry.pk).exists():
print("Entry contained in queryset")
Что будет быстрее, чем следующее, которое требует оценки и итерации по всему набору запросов:
if entry in some_queryset:
print("Entry contained in QuerySet")
И для проверки, содержит ли набор запросов какие-либо элементы:
if some_queryset.exists():
print("There is at least one object in some_queryset")
Что будет быстрее, чем:
if some_queryset:
print("There is at least one object in some_queryset")
…но не на большую степень (поэтому для получения прироста эффективности требуется большой набор запросов).
Кроме того, если набор запросов some_queryset ещё не был оценён, но вы знаете, что он будет оценён в какой-то момент, то использование some_queryset.exists() приведёт к большему объёму работы (один запрос для проверки существования плюс дополнительный запрос для последующего извлечения результатов), чем использование bool(some_queryset), которое извлекает результаты, а затем проверяет, были ли возвращены какие-либо результаты.
update()
-
update(**kwargs)
Выполняет запрос SQL update для указанных полей и возвращает количество сопоставленных строк (что может не равняться количеству обновлённых строк, если некоторые строки уже имеют новое значение).
Например, чтобы отключить комментарии для всех статей блога, опубликованных в 2010 году, вы можете сделать следующее:
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
(Предполагается, что ваша модель Entry имеет поля pub_date и comments_on.)
Вы можете обновить несколько полей — ограничений по количеству нет. Например, здесь мы обновляем поля comments_on и headline.
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False, headline='This is old')
Метод update() применяется мгновенно, и единственное ограничение для обновляемого QuerySet заключается в том, что он может обновлять только столбцы в основной таблице модели, а не в связанных моделях. Например, вы не можете сделать следующее:
>>> Entry.objects.update(blog__name='foo') # Won't work!
Однако фильтрация по связанным полям всё ещё возможна:
>>> Entry.objects.filter(blog__id=1).update(comments_on=True)
Вы не можете вызвать update() для QuerySet, над которым был выполнен срез или который по другим причинам больше нельзя фильтровать.
Метод update() возвращает количество затронутых строк:
>>> Entry.objects.filter(id=64).update(comments_on=True) 1 >>> Entry.objects.filter(slug='nonexistent-slug').update(comments_on=True) 0 >>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False) 132
Если вам просто нужно обновить запись и вы ничего не хотите делать с объектом модели, наиболее эффективным подходом является вызов update(), а не загрузка объекта модели в память. Например, вместо этого:
e = Entry.objects.get(id=10) e.comments_on = False e.save()
…сделайте так:
Entry.objects.filter(id=10).update(comments_on=False)
Использование update() также предотвращает состояние гонки, когда что-то может измениться в вашей базе данных в короткий промежуток времени между загрузкой объекта и вызовом save().
И наконец, учтите, что update() выполняет обновление на уровне SQL и, следовательно, не вызывает никаких методов save() в ваших моделях, а также не генерирует сигналы pre_save или post_save (которые являются следствием вызова Model.save()). Если вы хотите обновить множество записей для модели, у которой есть пользовательский метод save(), пройдитесь по ним и вызовите save() следующим образом:
for e in Entry.objects.filter(pub_date__year=2010):
e.comments_on = False
e.save()
delete()
-
delete()
Выполняет запрос 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, {'weblog.Entry': 2, 'weblog.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, {'weblog.Blog': 1, 'weblog.Entry': 2, 'weblog.Entry_authors': 2})
Это поведение каскадирования настраивается с помощью аргумента on_delete для ForeignKey.
Метод delete() выполняет массовое удаление и не вызывает никаких методов delete() в ваших моделях. Однако он генерирует сигналы pre_delete и post_delete для всех удалённых объектов (включая каскадные удаления).
Django необходимо загрузить объекты в память, чтобы отправлять сигналы и обрабатывать каскады. Однако если каскадов и сигналов нет, Django может использовать быстрый путь и удалять объекты без загрузки в память. При больших удалениях это может значительно сократить использование памяти. Также может уменьшиться количество выполненных запросов.
Внешние ключи, установленные в on_delete DO_NOTHING не препятствуют использованию быстрого пути при удалении.
Обратите внимание, что запросы, сгенерированные при удалении объектов, являются реализационным деталью, которая может изменяться.
as_manager()
-
classmethod as_manager()
Метод класса, возвращающий экземпляр Manager с копией методов QuerySet. Дополнительные сведения см. в Создание менеджера с методами QuerySet.
explain()
-
explain(format=None, **options)
Возвращает строку с планом выполнения запроса QuerySet, в которой подробно описано, как база данных будет выполнять запрос, включая используемые индексы и соединения. Знание этих деталей может помочь вам улучшить производительность медленных запросов.
Например, при использовании PostgreSQL:
>>> print(Blog.objects.filter(title='My Blog').explain()) Seq Scan on blog (cost=0.00..35.50 rows=10 width=12) Filter: (title = 'My Blog'::bpchar)
Вывод значительно отличается между базами данных.
explain() поддерживается всеми встроенными бэкендами баз данных, кроме Oracle, поскольку реализация там не очевидна.
Параметр format изменяет формат вывода из значения по умолчанию базы данных, который обычно текстовый. PostgreSQL поддерживает форматы 'TEXT', 'JSON', 'YAML' и 'XML'. MariaDB и MySQL поддерживают форматы 'TEXT' (также называемый 'TRADITIONAL') и 'JSON'. MySQL 8.0.16+ также поддерживает улучшенный формат 'TREE', похожий на вывод 'TEXT' PostgreSQL, и используется по умолчанию, если поддерживается.
Некоторые базы данных принимают флаги, которые могут возвращать больше информации о запросе. Передайте эти флаги в качестве аргументов ключевых слов. Например, при использовании PostgreSQL:
>>> print(Blog.objects.filter(title='My Blog').explain(verbose=True, analyze=True)) Seq Scan on public.blog (cost=0.00..35.50 rows=10 width=12) (actual time=0.004..0.004 rows=10 loops=1) Output: id, title Filter: (blog.title = 'My Blog'::bpchar) Planning time: 0.064 ms Execution time: 0.058 ms
В некоторых базах данных флаги могут привести к выполнению запроса, что может оказать нежелательное влияние на вашу базу данных. Например, флаг ANALYZE, поддерживаемый MariaDB, MySQL 8.0.18+ и PostgreSQL, может привести к изменениям данных, если есть триггеры или вызывается функция, даже для запроса SELECT.
Была добавлена поддержка формата 'TREE' в MySQL 8.0.16+ и параметра analyze в MariaDB и MySQL 8.0.18+.
Field lookup
Lookup полей — способ указать суть SQL WHERE условия. Они указываются в качестве аргументов ключевых слов для методов QuerySet filter(), exclude() и get().
Для ознакомления см. Документацию по моделям и запросам к базе данных.
Встроенные lookups Django перечислены ниже. Также можно создавать пользовательские lookups для полей моделей.
Для удобства, если тип lookup не указан (как в Entry.objects.get(id=14)), тип lookup по умолчанию — 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
В заданном итерируемом объекте; часто это список, кортеж или объект QuerySet. Это не часто используемый случай, но строки (являясь итерируемыми объектами) принимаются.
Примеры:
Entry.objects.filter(id__in=[1, 3, 4]) Entry.objects.filter(headline__in='abc')
SQL эквиваленты:
SELECT ... WHERE id IN (1, 3, 4);
SELECT ... WHERE headline IN ('a', 'b', 'c');
Вы также можете использовать объект QuerySet для динамической оценки списка значений вместо предоставления списка буквальных значений:
inner_qs = Blog.objects.filter(name__contains='Cheddar') entries = Entry.objects.filter(blog__in=inner_qs)
Этот объект QuerySet будет оценён как подзапрос:
SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')
Если вы передадите QuerySet , полученный в результате values() или values_list() , в качестве значения для поиска __in , убедитесь, что из результата извлекается только одно поле. Например, это сработает (фильтрация по именам блогов):
inner_qs = Blog.objects.filter(name__contains='Ch').values('name')
entries = Entry.objects.filter(blog__name__in=inner_qs)
Этот пример выведет исключение, поскольку внутренний запрос пытается извлечь два значения поля, тогда как ожидается только одно:
# Bad code! Will raise a TypeError.
inner_qs = Blog.objects.filter(name__contains='Ch').values('name', 'id')
entries = Entry.objects.filter(blog__name__in=inner_qs)
Учёт производительности
Будьте осторожны при использовании вложенных запросов и понимайте характеристики производительности вашего сервера базы данных (при необходимости делайте бенчмаркинг!). Некоторые бэкенды баз данных, в частности MySQL, не очень хорошо оптимизируют вложенные запросы. В таких случаях эффективнее извлечь список значений и затем передать его во второй запрос. То есть, выполните два запроса вместо одного:
values = Blog.objects.filter(
name__contains='Cheddar').values_list('pk', flat=True)
entries = Entry.objects.filter(blog__in=list(values))
Обратите внимание на list() вызов вокруг Blog QuerySet , чтобы принудительно выполнить первый запрос. Без него был бы выполнен вложенный запрос, потому что объекты QuerySet ленивы.
gt
Больше, чем.
Пример:
Entry.objects.filter(id__gt=4)
SQL эквивалент:
SELECT ... WHERE id > 4;
gte
Больше или равно.
lt
Меньше, чем.
lte
Меньше или равно.
startswith
Чувствительный к регистру поиск по началу строки.
Пример:
Entry.objects.filter(headline__startswith='Lennon')
SQL эквивалент:
SELECT ... WHERE headline LIKE 'Lennon%';
SQLite не поддерживает операторы LIKE с учётом регистра; startswith ведет себя как istartswith для SQLite.
istartswith
Нечувствительный к регистру поиск по началу строки.
Пример:
Entry.objects.filter(headline__istartswith='Lennon')
SQL эквивалент:
SELECT ... WHERE headline ILIKE 'Lennon%';
Пользователи SQLite
При использовании SQLite-бекенда и строк, отличных от ASCII, имейте в виду примечание по базе данных о сравнении строк.
endswith
Чувствительный к регистру поиск по окончанию строки.
Пример:
Entry.objects.filter(headline__endswith='Lennon')
SQL эквивалент:
SELECT ... WHERE headline LIKE '%Lennon';
Пользователи SQLite
SQLite не поддерживает операторы LIKE с учётом регистра; endswith ведет себя как iendswith для SQLite. Обратитесь к документации примечания по базе данных для получения более подробной информации.
iendswith
Нечувствительный к регистру поиск по окончанию строки.
Пример:
Entry.objects.filter(headline__iendswith='Lennon')
SQL эквивалент:
SELECT ... WHERE headline ILIKE '%Lennon'
Пользователи SQLite
При использовании SQLite-бекенда и строк, отличных от ASCII, имейте в виду примечание по базе данных о сравнении строк.
range
Тест диапазона (включая границы).
Пример:
import datetime start_date = datetime.date(2005, 1, 1) end_date = datetime.date(2005, 3, 31) Entry.objects.filter(pub_date__range=(start_date, end_date))
SQL эквивалент:
SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';
Вы можете использовать range везде, где в SQL можно использовать BETWEEN — для дат, чисел и даже символов.
Предупреждение
Фильтрация DateTimeField по датам не будет включать элементы последнего дня, потому что границы интерпретируются как «0:00 по данному времени». Если pub_date была DateTimeField, то вышеприведенное выражение было бы преобразовано в следующий SQL:
SELECT ... WHERE pub_date BETWEEN '2005-01-01 00:00:00' and '2005-03-31 00:00:00';
Как правило, нельзя смешивать даты и datetime.
date
Для полей datetime преобразует значение как дату. Разрешает цепочку дополнительных поисков по полям. Принимает значение даты.
Пример:
Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1)) Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
(Для этого поиска не включён фрагмент эквивалентного кода SQL, поскольку реализация соответствующего запроса различается в разных движках баз данных.)
Если USE_TZ равно True, поля преобразуются в текущую часовую зону перед фильтрацией. Это требует определения часовых поясов в базе данных.
year
Для полей date и datetime — точное совпадение года. Разрешает цепочку дополнительных поисков по полям. Принимает целое значение года.
Пример:
Entry.objects.filter(pub_date__year=2005) Entry.objects.filter(pub_date__year__gte=2005)
SQL эквивалент:
SELECT ... WHERE pub_date BETWEEN '2005-01-01' AND '2005-12-31'; SELECT ... WHERE pub_date >= '2005-01-01';
(Точная синтаксическая конструкция SQL варьируется для каждого движка базы данных.)
Если USE_TZ равно True, поля datetime преобразуются в текущую часовую зону перед фильтрацией. Это требует определения часовых поясов в базе данных.
iso_year
Для полей date и datetime — точное совпадение года по ISO 8601. Разрешает цепочку дополнительных поисков по полям. Принимает целое значение года.
Пример:
Entry.objects.filter(pub_date__iso_year=2005) Entry.objects.filter(pub_date__iso_year__gte=2005)
(Точная синтаксическая конструкция SQL варьируется для каждого движка базы данных.)
Если USE_TZ равно True, поля datetime преобразуются в текущую часовую зону перед фильтрацией. Это требует определения часовых поясов в базе данных.
month
Для полей date и datetime — точное совпадение месяца. Разрешает цепочку дополнительных поисков по полям. Принимает целое значение от 1 (январь) до 12 (декабрь).
Пример:
Entry.objects.filter(pub_date__month=12) Entry.objects.filter(pub_date__month__gte=6)
SQL эквивалент:
SELECT ... WHERE EXTRACT('month' FROM pub_date) = '12';
SELECT ... WHERE EXTRACT('month' FROM pub_date) >= '6';
(Точная синтаксическая конструкция SQL варьируется для каждого движка базы данных.)
Если USE_TZ равно True, поля datetime преобразуются в текущую часовую зону перед фильтрацией. Это требует определения часовых поясов в базе данных.
day
Для полей date и datetime — точное совпадение дня. Разрешает цепочку дополнительных поисков по полям. Принимает целое значение дня.
Пример:
Entry.objects.filter(pub_date__day=3) Entry.objects.filter(pub_date__day__gte=3)
SQL эквивалент:
SELECT ... WHERE EXTRACT('day' FROM pub_date) = '3';
SELECT ... WHERE EXTRACT('day' FROM pub_date) >= '3';
(Точная синтаксическая конструкция SQL варьируется для каждого движка базы данных.)
Это будет соответствовать любой записи с pub_date на третье число месяца, например, 3 января, 3 июля и т. д.
Если USE_TZ равно True, поля datetime преобразуются в текущую часовую зону перед фильтрацией. Это требует определения часовых поясов в базе данных.
week
Для полей date и datetime возвращает номер недели (1-52 или 53) в соответствии с ISO-8601, т.е. недели начинаются с понедельника, и первая неделя содержит первый четверг года.
Пример:
Entry.objects.filter(pub_date__week=52) Entry.objects.filter(pub_date__week__gte=32, pub_date__week__lte=38)
(Для этого поиска не включён фрагмент эквивалентного кода SQL, поскольку реализация соответствующего запроса различается в разных движках баз данных.)
Если USE_TZ равно True, поля datetime преобразуются в текущую часовую зону перед фильтрацией. Это требует определения часовых поясов в базе данных.
week_day
Для полей date и datetime совпадение по «дню недели». Разрешает цепочку дополнительных поисков по полям.
Принимает целое значение, представляющее день недели от 1 (воскресенье) до 7 (суббота).
Пример:
Entry.objects.filter(pub_date__week_day=2) Entry.objects.filter(pub_date__week_day__gte=2)
(Для этого поиска не включён фрагмент эквивалентного кода SQL, поскольку реализация соответствующего запроса различается в разных движках баз данных.)
Это соответствует любой записи с pub_date , которая выпадает на понедельник (день 2 недели), независимо от месяца или года, в котором это происходит. Дни недели индексируются, причём 1 — воскресенье, а 7 — суббота.
Когда USE_TZ равно True, поля datetime преобразуются в текущую временную зону до фильтрации. Это требует определений временных зон в базе данных.
iso_week_day
Для полей date и datetime требуется точное соответствие дня недели по ISO 8601. Разрешает цепочку дополнительных поисков по полям.
Принимает целое число, представляющее день недели от 1 (понедельник) до 7 (воскресенье).
Пример:
Entry.objects.filter(pub_date__iso_week_day=1) Entry.objects.filter(pub_date__iso_week_day__gte=1)
(Для этого поиска не включен эквивалентный фрагмент кода SQL, так как реализация соответствующего запроса варьируется в зависимости от различных баз данных.)
Обратите внимание, что это будет соответствовать любой записи с pub_date , которая попадает на понедельник (день 1 недели), независимо от месяца или года, в котором это происходит. Дни недели индексируются, причём день 1 — понедельник, а день 7 — воскресенье.
Когда USE_TZ равно True, поля datetime преобразуются в текущую временную зону до фильтрации. Это требует определений временных зон в базе данных.
quarter
Для полей date и datetime требуется соответствие «четверти года». Разрешает цепочку дополнительных поисков по полям. Принимает целое число от 1 до 4, представляющее четверть года.
Пример, чтобы получить записи во второй четверти (с 1 апреля по 30 июня):
Entry.objects.filter(pub_date__quarter=2)
(Для этого поиска не включен эквивалентный фрагмент кода SQL, так как реализация соответствующего запроса варьируется в зависимости от различных баз данных.)
Когда USE_TZ равно True, поля datetime преобразуются в текущую временную зону до фильтрации. Это требует определений временных зон в базе данных.
time
Для полей datetime значение преобразуется в значение типа time. Разрешает цепочку дополнительных поисков по полям. Принимает значение типа datetime.time.
Пример:
Entry.objects.filter(pub_date__time=datetime.time(14, 30)) Entry.objects.filter(pub_date__time__range=(datetime.time(8), datetime.time(17)))
(Для этого поиска не включен эквивалентный фрагмент кода SQL, так как реализация соответствующего запроса варьируется в зависимости от различных баз данных.)
Когда USE_TZ равно True, поля преобразуются в текущую временную зону до фильтрации. Это требует определений временных зон в базе данных.
hour
Для полей datetime и time требуется точное соответствие часа. Разрешает цепочку дополнительных поисков по полям. Принимает целое число от 0 до 23.
Пример:
Event.objects.filter(timestamp__hour=23) Event.objects.filter(time__hour=5) Event.objects.filter(timestamp__hour__gte=12)
Эквивалент в SQL:
SELECT ... WHERE EXTRACT('hour' FROM timestamp) = '23';
SELECT ... WHERE EXTRACT('hour' FROM time) = '5';
SELECT ... WHERE EXTRACT('hour' FROM timestamp) >= '12';
(Точная синтаксическая конструкция SQL варьируется для каждой базы данных.)
Когда USE_TZ равно True, поля datetime преобразуются в текущую временную зону до фильтрации. Это требует определений временных зон в базе данных.
minute
Для полей datetime и time требуется точное соответствие минуты. Разрешает цепочку дополнительных поисков по полям. Принимает целое число от 0 до 59.
Пример:
Event.objects.filter(timestamp__minute=29) Event.objects.filter(time__minute=46) Event.objects.filter(timestamp__minute__gte=29)
Эквивалент в SQL:
SELECT ... WHERE EXTRACT('minute' FROM timestamp) = '29';
SELECT ... WHERE EXTRACT('minute' FROM time) = '46';
SELECT ... WHERE EXTRACT('minute' FROM timestamp) >= '29';
(Точная синтаксическая конструкция SQL варьируется для каждой базы данных.)
Когда USE_TZ равно True, поля datetime преобразуются в текущую временную зону до фильтрации. Это требует определений временных зон в базе данных.
second
Для полей datetime и time требуется точное соответствие секунды. Разрешает цепочку дополнительных поисков по полям. Принимает целое число от 0 до 59.
Пример:
Event.objects.filter(timestamp__second=31) Event.objects.filter(time__second=2) Event.objects.filter(timestamp__second__gte=31)
Эквивалент в SQL:
SELECT ... WHERE EXTRACT('second' FROM timestamp) = '31';
SELECT ... WHERE EXTRACT('second' FROM time) = '2';
SELECT ... WHERE EXTRACT('second' FROM timestamp) >= '31';
(Точная синтаксическая конструкция SQL варьируется для каждой базы данных.)
Когда USE_TZ равно True, поля datetime преобразуются в текущую временную зону до фильтрации. Это требует определений временных зон в базе данных.
isnull
Принимает значения True или False, которые соответствуют запросам SQL IS NULL и IS NOT NULL соответственно.
Пример:
Entry.objects.filter(pub_date__isnull=True)
Эквивалент в SQL:
SELECT ... WHERE pub_date IS NULL;
Устарело начиная с версии 3.1: Использование значений, отличных от булевых, в правой части устарело, используйте True или False вместо этого. В Django 4.0 будет возбуждаться исключение.
regex
Сопоставление по шаблону регулярных выражений, учитывающее регистр.
Синтаксис регулярных выражений соответствует используемому обратному модулю базы данных. В случае SQLite, у которого нет встроенной поддержки регулярных выражений, эта функция предоставляется пользовательской функцией REGEXP (на Python), и синтаксис регулярных выражений соответствует синтаксису модуля re Python.
Пример:
Entry.objects.get(title__regex=r'^(An?|The) +')
Эквиваленты в SQL:
SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL SELECT ... WHERE REGEXP_LIKE(title, '^(An?|The) +', 'c'); -- Oracle SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite
Рекомендуется использовать сырые строки (например, r'foo' вместо 'foo') для передачи синтаксиса регулярных выражений.
iregex
Сопоставление по шаблону регулярных выражений, не учитывающее регистр.
Пример:
Entry.objects.get(title__iregex=r'^(an?|the) +')
Эквиваленты в SQL:
SELECT ... WHERE title REGEXP '^(an?|the) +'; -- MySQL SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'i'); -- Oracle SELECT ... WHERE title ~* '^(an?|the) +'; -- PostgreSQL SELECT ... WHERE title REGEXP '(?i)^(an?|the) +'; -- SQLite
Функции агрегирования
Django предоставляет следующие функции агрегирования в модуле django.db.models. Подробные сведения о том, как использовать эти функции агрегирования, см. в руководстве по теме агрегирования. См. документацию по Aggregate, чтобы узнать, как создать собственные агрегаты.
Предупреждение
SQLite не поддерживает агрегирование по полям date/time «из коробки». Это связано с тем, что в SQLite нет встроенных полей date/time, и Django в настоящее время эмулирует эти возможности, используя текстовое поле. Попытки использовать агрегирование по полям date/time в SQLite приведут к ошибке NotSupportedError.
Примечание
Функции агрегирования возвращают None при использовании с пустым QuerySet. Например, функция агрегирования Sum возвращает None вместо 0 , если QuerySet не содержит записей. Исключение составляет Count, которая возвращает 0 , если QuerySet пусто.
Все агрегаты имеют следующие общие параметры:
expressions
Строки, которые ссылаются на поля модели или выражения запроса.
output_field
Необязательный аргумент, представляющий поле модели возвращаемого значения.
Примечание
При объединении нескольких типов полей Django может определить output_field только если все поля имеют один и тот же тип. В противном случае вы должны сами указать output_field.
filter
Необязательный Q object, который используется для фильтрации строк, подвергаемых агрегированию.
См. Условное агрегирование и Фильтрация по аннотациям для примеров использования.
**extra
Ключевые аргументы, которые могут предоставить дополнительный контекст для SQL, сгенерированного агрегатом.
Avg
-
class Avg(expression, output_field=None, distinct=False, filter=None, **extra) -
Возвращает среднее значение данного выражения, которое должно быть числовым, если вы не укажете другое
output_field.- Значение по умолчанию для алиаса:
<field>__avg - Тип возвращаемого значения:
floatесли вход —int, в противном случае — такой же, как у входного поля, илиoutput_fieldесли предоставлен
Имеет один необязательный аргумент:
-
distinct -
Если
distinct=True,Avgвозвращает среднее значение уникальных значений. Это эквивалент SQL запросуAVG(DISTINCT <field>). Значение по умолчанию —False.
Изменено в Django 3.0:Была добавлена поддержка
distinct=True. - Значение по умолчанию для алиаса:
Count
-
class Count(expression, distinct=False, filter=None, **extra) -
Возвращает количество объектов, связанных через предоставленное выражение.
- Значение по умолчанию для алиаса:
<field>__count - Тип возвращаемого значения:
int
Имеет один необязательный аргумент:
-
distinct -
Если
distinct=True, подсчёт будет учитывать только уникальные экземпляры. Это эквивалент SQL запросуCOUNT(DISTINCT <field>). Значение по умолчанию —False.
- Значение по умолчанию для алиаса:
Max
-
class Max(expression, output_field=None, filter=None, **extra) -
Возвращает максимальное значение заданного выражения.
- По умолчанию псевдоним:
<field>__max - Тип возвращаемого значения: такой же, как у входного поля, или
output_field, если он указан
- По умолчанию псевдоним:
Min
-
class Min(expression, output_field=None, filter=None, **extra) -
Возвращает минимальное значение заданного выражения.
- По умолчанию псевдоним:
<field>__min - Тип возвращаемого значения: такой же, как у входного поля, или
output_field, если он указан
- По умолчанию псевдоним:
StdDev
-
class StdDev(expression, output_field=None, sample=False, filter=None, **extra) -
Возвращает стандартное отклонение данных в указанном выражении.
- По умолчанию псевдоним:
<field>__stddev - Тип возвращаемого значения:
float, если входное значениеint, в противном случае такой же, как у входного поля, илиoutput_field, если он указан
Есть один необязательный аргумент:
-
sample -
По умолчанию,
StdDevвозвращает стандартное отклонение генеральной совокупности. Однако, еслиsample=True, возвращаемое значение будет стандартным отклонением выборки.
- По умолчанию псевдоним:
Sum
-
class Sum(expression, output_field=None, distinct=False, filter=None, **extra) -
Вычисляет сумму всех значений заданного выражения.
- По умолчанию псевдоним:
<field>__sum - Тип возвращаемого значения: такой же, как у входного поля, или
output_field, если он указан
Есть один необязательный аргумент:
-
distinct -
Если
distinct=True,Sumвозвращает сумму уникальных значений. Это эквивалент SQL-запросаSUM(DISTINCT <field>). Значение по умолчаниюFalse.
Изменено в Django 3.0:Была добавлена поддержка
distinct=True. - По умолчанию псевдоним:
Variance
-
class Variance(expression, output_field=None, sample=False, filter=None, **extra) -
Возвращает дисперсию данных в указанном выражении.
- По умолчанию псевдоним:
<field>__variance - Тип возвращаемого значения:
float, если входное значениеint, в противном случае такой же, как у входного поля, илиoutput_field, если он указан
Есть один необязательный аргумент:
-
sample -
По умолчанию,
Varianceвозвращает дисперсию генеральной совокупности. Однако, еслиsample=True, возвращаемое значение будет дисперсией выборки.
- По умолчанию псевдоним:
Средства, связанные с запросами
Q() объекты
-
class Q
Объект Q() представляет собой SQL-условие, которое может использоваться в операциях, связанных с базой данных. Он похож на то, как объект F() представляет значение поля модели или аннотации. Они позволяют определять и повторно использовать условия и комбинировать их с помощью операторов, таких как | (OR) и & (AND). См. Сложные запросы с объектами Q.
Prefetch() объекты
-
class Prefetch(lookup, queryset=None, to_attr=None)
Объект Prefetch() может использоваться для управления операцией prefetch_related().
Аргумент lookup описывает отношения для отслеживания и работает так же, как строковые запросы, передаваемые в prefetch_related(). Например:
>>> from django.db.models import Prefetch
>>> Question.objects.prefetch_related(Prefetch('choice_set')).get().choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
# This will only execute two queries regardless of the number of Question
# and Choice objects.
>>> Question.objects.prefetch_related(Prefetch('choice_set')).all()
<QuerySet [<Question: What's up?>]>
Аргумент queryset предоставляет базовый QuerySet для заданного запроса. Это полезно для дальнейшего фильтрации операции предварительной выборки или вызова select_related() из предварительно выбранного отношения, тем самым ещё больше уменьшая количество запросов:
>>> voted_choices = Choice.objects.filter(votes__gt=0)
>>> voted_choices
<QuerySet [<Choice: The sky>]>
>>> prefetch = Prefetch('choice_set', queryset=voted_choices)
>>> Question.objects.prefetch_related(prefetch).get().choice_set.all()
<QuerySet [<Choice: The sky>]>
Аргумент to_attr задает результат предварительной выборки в пользовательском атрибуте:
>>> prefetch = Prefetch('choice_set', queryset=voted_choices, to_attr='voted_choices')
>>> Question.objects.prefetch_related(prefetch).get().voted_choices
[<Choice: The sky>]
>>> Question.objects.prefetch_related(prefetch).get().choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
Примечание
При использовании to_attr предварительно выбранный результат хранится в списке. Это может значительно улучшить скорость по сравнению с традиционными вызовами prefetch_related, которые хранят кэшированный результат внутри экземпляра QuerySet.
prefetch_related_objects()
Предварительно выбирает указанные запросы в итерируемом наборе экземпляров моделей. Это полезно в коде, который получает список экземпляров моделей, а не QuerySet, например, при извлечении моделей из кэша или при их ручном создании.
Передайте итерируемый набор экземпляров модели (все они должны быть одного класса) и запросы или объекты Prefetch, которые вы хотите предварительно выбрать. Например:
>>> from django.db.models import prefetch_related_objects >>> restaurants = fetch_top_restaurants_from_cache() # A list of Restaurants >>> prefetch_related_objects(restaurants, 'pizzas__toppings')
При использовании нескольких баз данных с prefetch_related_objects, запрос предварительной выборки будет использовать базу данных, связанную с экземпляром модели. Это можно переопределить, используя пользовательский набор запросов в связанном запросе.
FilteredRelation() объекты
-
class FilteredRelation(relation_name, *, condition=Q()) -
-
relation_name -
Имя поля, по которому вы хотите отфильтровать отношение.
-
condition -
Объект
Qдля управления фильтрацией.
-
FilteredRelation используется с annotate() для создания ON условия при выполнении JOIN. Он не действует на стандартное отношение, а на имя аннотации (pizzas_vegetarian в примере ниже).
Например, чтобы найти рестораны, в которых есть вегетарианские пиццы с 'mozzarella' в названии:
>>> from django.db.models import FilteredRelation, Q >>> Restaurant.objects.annotate( ... pizzas_vegetarian=FilteredRelation( ... 'pizzas', condition=Q(pizzas__vegetarian=True), ... ), ... ).filter(pizzas_vegetarian__name__icontains='mozzarella')
Если пицц большое количество, этот запрос выполняется быстрее, чем:
>>> Restaurant.objects.filter( ... pizzas__vegetarian=True, ... pizzas__name__icontains='mozzarella', ... )
потому что фильтрация в WHERE разделе первого набора запросов будет выполняться только для вегетарианских пицц.
FilteredRelation не поддерживает:
-
Условий, которые охватывают связанные поля. Например:
>>> Restaurant.objects.annotate( ... pizzas_with_toppings_startswith_n=FilteredRelation( ... 'pizzas__toppings', ... condition=Q(pizzas__toppings__name__startswith='n'), ... ), ... ) Traceback (most recent call last): ... ValueError: FilteredRelation's condition doesn't support nested relations (got 'pizzas__toppings__name__startswith').
-
QuerySet.only()иprefetch_related(). - Атрибут
GenericForeignKey, унаследованный от родительской модели.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/3.1/ref/models/querysets/