Spec-Zone.ru › Django 2.2

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

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

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

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

QuerySet API

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

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

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

Класс 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), которые управляют тем, как сортируются значения NULL.

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

Примечание

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

Рассмотрим этот случай:

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

Event.objects.order_by('children__date')

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

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

Нет способа указать, должна ли сортировка быть чувствительной к регистру. Что касается регистровой чувствительности, Django будет сортировать результаты так, как обычно делает ваш бэкенд базы данных.

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

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

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

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

Каждый вызов 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() обычно следует вызывать только для набора результатов запроса, имеющего определённую сортировку (например, при запросе к модели, в которой определена стандартная сортировка, или при использовании order_by()). Если для данного QuerySet такая сортировка не определена, вызов reverse() на нём не оказывает существенного влияния (сортировка была неопределённой до вызова reverse(), и останется неопределённой после).

distinct()

distinct(*fields)

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

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

Примечание

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

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

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

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

Примечание

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

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

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

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

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

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

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

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

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

Примечание

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

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

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

values()

values(*fields, **expressions)

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

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

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

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

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

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

Пример:

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

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

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

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

>>> from django.db.models import CharField
>>> from django.db.models.functions import Lower
>>> CharField.register_lookup(Lower)
>>> Blog.objects.values('name__lower')
<QuerySet [{'name__lower': 'beatles blog'}]>
Изменено в Django 2.1:

Добавлена поддержка поисковых запросов.

Агрегат в 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() не имеет смысла, поэтому это вызовет NotImplementedError.
  • Комбинирование преобразований и агрегатов требует использования двух вызовов 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)]
Изменено в Django 2.1:

Добавлена поддержка «недели».

datetimes()

datetimes(field_name, kind, order='ASC', tzinfo=None)

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

field_name должно быть именем DateTimeField вашей модели.

kind должно быть либо "year", "month", "week", "day", "hour", "minute", или "second". Каждый объект datetime.datetime в списке результатов «обрезается» до заданной type.

order, по умолчанию 'ASC', должно быть либо 'ASC' или 'DESC'. Это указывает, как упорядочить результаты.

tzinfo определяет часовой пояс, в который преобразуются даты и время до обрезки. Действительно, заданное значение datetime имеет разные представления в зависимости от используемого часового пояса. Этот параметр должен быть объектом datetime.tzinfo. Если это None, Django использует текущий часовой пояс. Он не влияет, когда USE_TZ имеет значение False.

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

Добавлена поддержка «недели».

Примечание

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

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

none()

none()

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

select_related(*fields)

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

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

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

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

А вот поиск с select_related:

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

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

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

from django.utils import timezone

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

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

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

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

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

from django.db import models

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

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

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

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

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

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

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

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

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

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

>>> without_relations = queryset.select_related(None)

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

prefetch_related()

prefetch_related(*lookups)

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

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

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

prefetch_related, с другой стороны, выполняет отдельный поиск для каждого отношения и выполняет «соединение» в Python. Это позволяет ему предварительно извлекать объекты «многие ко многим» и «многие к одному», что невозможно с помощью select_related, а также отношения «один к одному» и внешние ключи, которые поддерживаются select_related. Он также поддерживает предварительное извлечение GenericRelation и GenericForeignKey, однако, он должен быть ограничен однородным набором результатов. Например, предварительное извлечение объектов, на которые ссылается GenericForeignKey поддерживается только если запрос ограничен одним ContentType.

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

from django.db import models

class Topping(models.Model):
    name = models.CharField(max_length=30)

class Pizza(models.Model):
    name = models.CharField(max_length=50)
    toppings = models.ManyToManyField(Topping)

    def __str__(self):
        return "%s (%s)" % (
            self.name,
            ", ".join(topping.name for topping in self.toppings.all()),
        )

и вы выполняете:

>>> Pizza.objects.all()
["Hawaiian (ham, pineapple)", "Seafood (prawns, smoked salmon)"...

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

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

>>> Pizza.objects.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')))

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

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

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

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

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

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

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

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

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

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

Примечание

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

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

>>> prefetch_related('pizzas__toppings', 'pizzas')

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

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

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

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

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

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

extra()

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

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

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

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

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

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

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

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

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

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

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

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

"select col from sometable where othercol = '%s'"  # unsafe!

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

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

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

  • select

    Аргумент select позволяет добавлять дополнительные поля в SELECT предложение. Он должен быть словарем, сопоставляющим имена атрибутов с SQL-предложениями для расчёта этого атрибута.

    Пример:

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

    В результате каждый объект Entry получит дополнительный атрибут, is_recent, булево значение, обозначающее, что pub_date записи больше 1 января 2006 года.

    Django вставляет предоставленный фрагмент SQL напрямую в SELECT оператор, поэтому результирующий SQL-запрос в приведённом примере будет чем-то вроде:

    SELECT blog_entry.*, (pub_date > '2006-01-01') AS is_recent
    FROM blog_entry;
    

    Следующий пример более сложный; он выполняет подзапрос, чтобы присвоить каждому результативному объекту Blog атрибут entry_count, целочисленное значение количества связанных объектов Entry.

    Blog.objects.extra(
        select={
            'entry_count': 'SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id'
        },
    )
    

    В этом конкретном случае мы используем тот факт, что запрос уже содержит таблицу blog_blog в FROM предложении.

    Результирующий SQL-запрос в приведённом примере будет:

    SELECT blog_blog.*, (SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id) AS entry_count
    FROM blog_blog;
    

    Обратите внимание, что скобки, необходимые большинству СУБД для подзапросов, не требуются в select предложениях Django. Также обратите внимание, что некоторые базы данных, такие как некоторые версии MySQL, не поддерживают подзапросы.

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

    Это будет работать, например:

    Blog.objects.extra(
        select=OrderedDict([('a', '%s'), ('b', '%s')]),
        select_params=('one', 'two'))
    

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

  • where / tables

    Вы можете определять явные SQL-WHERE предложения — возможно, для выполнения неявных соединений — с помощью where. Вы можете вручную добавлять таблицы в FROM предложение SQL, используя 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=())

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

Например:

from django.db import transaction

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

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

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

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

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

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

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

Вы не можете использовать select_for_update() для отношений с 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(). Однако MySQL не поддерживает аргумент of, а аргументы nowait и skip_locked поддерживаются только в MySQL 8.0.1+.

Передача nowait=True, of или skip_locked=True в 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 с помощью оператора AND SQL.

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

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 с помощью оператора OR SQL.

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

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)

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

get() вызывает MultipleObjectsReturned, если найдено более одного объекта. Исключение MultipleObjectsReturned является атрибутом класса модели.

get() вызывает исключение DoesNotExist, если для заданных параметров объект не найден. Это исключение является атрибутом класса модели. Пример:

Entry.objects.get(id='foo') # raises Entry.DoesNotExist

Исключение DoesNotExist наследуется от django.core.exceptions.ObjectDoesNotExist, поэтому вы можете обрабатывать несколько исключений DoesNotExist. Пример:

from django.core.exceptions import ObjectDoesNotExist
try:
    e = Entry.objects.get(id=3)
    b = Blog.objects.get(id=1)
except ObjectDoesNotExist:
    print("Either the entry or blog doesn't exist.")

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

entry = Entry.objects.filter(...).exclude(...).get()

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.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)

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

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

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

  • Метод модели save() не будет вызван, и сигналы pre_save и post_save не будут отправлены.
  • Он не работает с дочерними моделями в сценарии наследования по нескольким таблицам.
  • Если первичный ключ модели является AutoField, он не извлекает и не устанавливает атрибут первичного ключа, как save(), если база данных не поддерживает эту функцию (в настоящее время PostgreSQL).
  • Он не работает с взаимосвязями "многие ко многим".
  • Он преобразует 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 переменных на запрос.

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

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

Был добавлен параметр ignore_conflicts.

bulk_update()

Новое в Django 2.2.
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) по результатам. Набор запросов обычно кэширует свои результаты, чтобы повторные вычисления не приводили к дополнительным запросам. В отличие от этого, iterator() будет читать результаты непосредственно, без кэширования на уровне QuerySet (внутренне, итератор по умолчанию вызывает iterator() и кэширует возвращаемое значение). Для набора запросов, который возвращает большое количество объектов, к которым вам нужно получить доступ только один раз, это может повысить производительность и значительно сократить использование памяти.

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

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

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

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

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:

Предполагая строки из 10-20 столбцов со смешанными текстовыми и числовыми данными, 2000 будет извлекать менее 100 КБ данных, что кажется хорошим компромиссом между количеством строк, переданных и данными, отброшенными, если цикл будет прерван досрочно.
Изменено в Django 2.2:

Добавлена поддержка потоковой передачи результатов в SQLite.

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 так, как будто они больше, чем значения, отличные от null, в то время как SQLite делает наоборот.

Вы можете отфильтровать значения null:

Entry.objects.filter(pub_date__isnull=False).latest('pub_date')

earliest()

earliest(*fields)

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

first()

first()

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

Пример:

p = Article.objects.order_by('title', 'pub_date').first()

Обратите внимание, что first() — это удобный метод, следующий фрагмент кода эквивалентен вышеприведённому примеру:

try:
    p = Article.objects.order_by('title', 'pub_date')[0]
except IndexError:
    p = None

last()

last()

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

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

По умолчанию, ForeignKey Django эмулирует ограничение 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 для всех удалённых объектов (включая каскадные удаления).

END_OF_DOCUMENT_MARKER

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

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

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

as_manager()

classmethod as_manager()

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

explain()

Новое в Django 2.1.
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'. MySQL поддерживает 'TEXT' (также называемый 'TRADITIONAL') и 'JSON'.

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

>>> print(Blog.objects.filter(title='My Blog').explain(verbose=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 PostgreSQL может привести к изменениям данных, если существуют триггеры или вызывается функция, даже для SELECT запроса.

Field запросы

Полевые запросы — это способ указать суть части SQL WHERE . Они указываются в качестве ключевых аргументов для методов QuerySet filter(), exclude() и get().

Для введения см. Документацию по моделям и базам данных.

Встроенные запросы Django перечислены ниже. Также возможно написать пользовательские запросы для полей модели.

Для удобства, если тип запроса не задан (как в Entry.objects.get(id=14) ), тип запроса предполагается exact.

exact

Полное совпадение. Если предоставленное для сравнения значение равно None, оно будет интерпретировано как SQL NULL (см. isnull для получения дополнительной информации).

Примеры:

Entry.objects.get(id__exact=14)
Entry.objects.get(id__exact=None)

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

SELECT ... WHERE id = 14;
SELECT ... WHERE id IS NULL;

Сравнения MySQL

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

iexact

Нечувствительное к регистру полное совпадение. Если предоставленное для сравнения значение равно None, оно будет интерпретировано как SQL NULL (см. isnull для получения дополнительной информации).

Пример:

Blog.objects.get(name__iexact='beatles blog')
Blog.objects.get(name__iexact=None)

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

SELECT ... WHERE name ILIKE 'beatles blog';
SELECT ... WHERE name IS NULL;

Обратите внимание, что первый запрос будет соответствовать 'Beatles Blog', 'beatles blog', 'BeAtLes BLoG', и т.д.

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

При использовании SQLite бэкенда и не-ASCII строк имейте в виду примечание к базе данных о сравнениях строк. SQLite не выполняет нечувствительное к регистру сопоставление для не-ASCII строк.

contains

Чувствительное к регистру соответствие по содержимому.

Пример:

Entry.objects.get(headline__contains='Lennon')

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

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

Обратите внимание, что это будет соответствовать заголовку 'Lennon honored today' , но не 'lennon honored today'.

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

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

icontains

Нечувствительное к регистру соответствие по содержимому.

Пример:

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

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

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

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

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

in

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

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

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

date

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

Пример:

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

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

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

year

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

Пример:

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

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

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

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

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

iso_year

Новое в Django 2.2.

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

Пример:

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

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

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

month

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

Пример:

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

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

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

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

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

day

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

Пример:

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

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

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

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

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

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

week

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

Пример:

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

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

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

week_day

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

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

Пример:

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

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

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

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

quarter

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

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

Entry.objects.filter(pub_date__quarter=2)

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

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

time

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

Пример:

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

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

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

hour

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

Пример:

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

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

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

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

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

minute

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

Пример:

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

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

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

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

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

second

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

Пример:

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

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

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

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

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

isnull

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

Пример:

Entry.objects.filter(pub_date__isnull=True)

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

SELECT ... WHERE pub_date IS NULL;

regex

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

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

Пример:

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

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

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

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

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

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

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

iregex

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

Пример:

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

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

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

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

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

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

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

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

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

SQLite не может обрабатывать агрегирование по полям даты/времени без дополнительной настройки. Это связано с тем, что в SQLite нет встроенных полей даты/времени, и Django в настоящее время эмулирует эти функции с помощью текстового поля. Попытки использовать агрегирование по полям даты/времени в SQLite приведут к ошибке NotImplementedError.

Примечание

Функции агрегирования возвращают 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, filter=None, **extra) [source]

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

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

Count

class Count(expression, distinct=False, filter=None, **extra) [source]

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

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

Имеет один необязательный аргумент:

distinct

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

Max

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

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

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

Min

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

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

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

StdDev

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

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

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

Имеет один необязательный аргумент:

sample

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

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

Добавлена поддержка SQLite.

Sum

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

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

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

Variance

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

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

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

Имеет один необязательный аргумент:

sample

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

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

Добавлена поддержка SQLite.

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

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

Q() объекты

class Q [source]

Объект Q(), как и объект F, инкапсулирует SQL-выражение в объекте Python, который можно использовать в операциях, связанных с базой данных.

В общем случае, Q() objects позволяют определить и повторно использовать условия. Это позволяет строить сложные запросы к базе данных, используя операторы | (OR) и & (AND) ; в частности, в противном случае невозможно использовать OR в QuerySets.

Prefetch() объекты

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

Объект Prefetch() может использоваться для управления выполнением prefetch_related().

Аргумент lookup описывает отношения для отслеживания и работает так же, как строковые обозначения, передаваемые prefetch_related(). Например:

>>> from django.db.models import Prefetch
>>> Question.objects.prefetch_related(Prefetch('choice_set')).get().choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
# This will only execute two queries regardless of the number of Question
# and Choice objects.
>>> Question.objects.prefetch_related(Prefetch('choice_set')).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()

prefetch_related_objects(model_instances, *related_lookups) [source]

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

FilteredRelation() объекты

class FilteredRelation(relation_name, *, condition=Q()) [source]
relation_name

Имя поля, по которому вы хотите отфильтровать отношение.

condition

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

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

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

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

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

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

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

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

  • Условий, которые охватывают связанные поля. Например:

    >>> 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/2.2/ref/models/querysets/

Spec-Zone.ru

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