Spec-Zone.ru › Django 3.0

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

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

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

QuerySet API

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

class QuerySet(model=None, query=None, using=None, hints=None)

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

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

ordered

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

db

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

Примечание

Параметр query для QuerySet существует для того, чтобы специализированные подклассы запросов могли восстановить внутреннее состояние запроса. Значение параметра - непрозрачное представление этого состояния запроса и не входит в состав публичного API. Другими словами: если вам нужно спросить, вам не нужно его использовать.

Методы, возвращающие новые QuerySet

Django предоставляет набор методов уточнения QuerySet , которые изменяют либо типы результатов, возвращаемых QuerySet, либо способ выполнения запроса SQL.

filter()

filter(**kwargs)

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

Параметры поиска (**kwargs) должны быть в формате, описанном в Поиск по полям ниже. Несколько параметров объединяются через AND в подлежащем SQL-запросе.

Если вам нужны более сложные запросы (например, запросы с OR операторами), вы можете использовать Q objects.

exclude()

exclude(**kwargs)

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

Параметры поиска (**kwargs) должны быть в формате, описанном в Поиск по полям ниже. Несколько параметров объединяются через AND в подлежащем SQL-запросе, и всё это заключено в NOT().

В этом примере исключаются все записи, у которых pub_date позже 2005-01-03 И у которых headline «Hello»:

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')

В терминах SQL это эквивалентно:

SELECT ...
WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')

В этом примере исключаются все записи, у которых pub_date позже 2005-01-03 ИЛИ заголовок «Hello»:

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline='Hello')

В терминах SQL это эквивалентно:

SELECT ...
WHERE NOT pub_date > '2005-1-3'
AND NOT headline = 'Hello'

Обратите внимание, что во втором примере ограничение более жёсткое.

Если вам нужны более сложные запросы (например, запросы с OR операторами), вы можете использовать Q objects.

annotate()

annotate(*args, **kwargs)

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

Каждый аргумент для annotate() — это аннотация, которая будет добавлена к каждому объекту в QuerySet, который возвращается.

Функции агрегации, предоставляемые Django, описаны ниже в разделе Функции агрегации.

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

Например, если вы работаете со списком блогов, вам может потребоваться определить, сколько записей сделано в каждом блоге:

>>> from django.db.models import Count
>>> q = Blog.objects.annotate(Count('entry'))
# The name of the first blog
>>> q[0].name
'Blogasaurus'
# The number of entries on the first blog
>>> q[0].entry__count
42

Модель Blog не определяет атрибут entry__count сама по себе, но с помощью именованного аргумента для указания функции агрегации вы можете контролировать имя аннотации:

>>> q = Blog.objects.annotate(number_of_entries=Count('entry'))
# The number of entries on the first blog, using the name provided
>>> q[0].number_of_entries
42

Для углубленного обсуждения агрегации см. руководство по теме агрегации.

order_by()

order_by(*fields)

По умолчанию результаты, возвращаемые QuerySet, упорядочиваются по кортежу сортировки, заданному параметром ordering в модели Meta. Вы можете переопределить это для каждого QuerySet с помощью метода order_by.

Пример:

Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')

Результат выше будет отсортирован по pub_date в порядке убывания, а затем по headline в порядке возрастания. Минус перед "-pub_date" указывает убывающий порядок. Возрастающий порядок подразумевается. Для случайной сортировки используйте "?", как показано ниже:

Entry.objects.order_by('?')

Примечание: запросы с order_by('?') могут быть дорогостоящими и медленными в зависимости от используемого СУБД.

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

Entry.objects.order_by('blog__name', 'headline')

Если вы пытаетесь отсортировать по полю, которое является отношением к другой модели, Django будет использовать стандартную сортировку связанной модели или сортировать по первичному ключу связанной модели, если не указан Meta.ordering. Например, так как модель Blog не имеет указанной по умолчанию сортировки:

Entry.objects.order_by('blog')

…тождественно:

Entry.objects.order_by('blog__id')

Если у Blog есть ordering = ['name'], то первый запрос будет идентичен:

Entry.objects.order_by('blog__name')

Вы также можете отсортировать по выражениям запроса, вызвав asc() или desc() для выражения:

Entry.objects.order_by(Coalesce('summary', 'headline').desc())

asc() и desc() имеют аргументы (nulls_first и nulls_last), которые управляют тем, как сортируются значения NULL.

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

Примечание

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

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

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

Event.objects.order_by('children__date')

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

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

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

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

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

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

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

Каждый вызов order_by() очистит предыдущую сортировку. Например, этот запрос будет отсортирован по pub_date и не по headline:

Entry.objects.order_by('headline').order_by('pub_date')

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

Сортировка — это не бесплатная операция. Каждое добавляемое поле в сортировку влечет за собой затраты для вашей базы данных. Каждое внешнее ключи будет неявно включать все свои стандартные сортировки.

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

reverse()

reverse()

Используйте метод reverse() для изменения порядка возвращаемых элементов запроса. Вызов reverse() второй раз восстанавливает порядок обратно в нормальное направление.

Чтобы получить «последние» пять элементов в наборе запросов, можно сделать так:

my_queryset.reverse()[:5]

Обратите внимание, что это не совсем то же самое, что срезы с конца последовательности в Python. Приведенный выше пример вернет последний элемент первым, затем предпоследний и так далее. Если бы у нас была последовательность Python и мы посмотрели на seq[-5:], мы бы увидели пятый с конца элемент первым. Django не поддерживает такой режим доступа (срез с конца), потому что это не эффективно в SQL.

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

distinct()

distinct(*fields)

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

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

Примечание

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

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

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

Только для PostgreSQL вы можете передать позиционные аргументы (*fields) для указания имён полей, к которым должна применяться DISTINCT. Это приводит к запросу 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'}]>

Агрегирование внутри 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)]

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.

Примечание

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

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

none()

none()

Вызов none() создаст запрос, который никогда не вернет никаких объектов, и запрос не будет выполнен при обращении к результатам. Запрос qs.none() — это экземпляр EmptyQuerySet.

Примеры:

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

all()

all()

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

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

union()

union(*other_qs, all=False)

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

Например:

>>> qs1.union(qs2, qs3)

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

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

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

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

Далее, базы данных накладывают ограничения на разрешенные операции в объединенных запросах. Например, большинство баз данных не допускают LIMIT или OFFSET в объединенных запросах.

intersection()

intersection(*other_qs)

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

Например:

>>> qs1.intersection(qs2, qs3)

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

difference()

difference(*other_qs)

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

Например:

>>> qs1.difference(qs2, qs3)

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

select_related()

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 QuerySet, чтобы позволить удалить extra(). Мы больше не улучшаем и не исправляем ошибки для этого метода.

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

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

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

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

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

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

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

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

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

Подробнее о том, как работает защита от атак SQL-инъекций Django, можно узнать на странице защиты от SQL-инъекций.

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

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

  • select

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

    Пример:

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

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

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

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

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

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

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

    Результирующий SQL приведенного выше примера будет:

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

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

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

    Это сработает, например:

    Blog.objects.extra(
        select={'a': '%s', 'b': '%s'},
        select_params=('one', 'two'),
    )
    

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

  • where / tables

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

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

    Пример:

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

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

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

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

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

  • order_by

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

    Например:

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

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

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

  • params

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

    Пример:

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

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

    Плохо:

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

    Хорошо:

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

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

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

defer()

defer(*fields)

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

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

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

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

Вы можете сделать несколько вызовов 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 relations:

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

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

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

В настоящее время бэкэнды баз данных postgresql, oracle, и mysql поддерживают select_for_update(). Однако MariaDB 10.3+ поддерживает только аргумент nowait, а MySQL 8.0.1+ поддерживает аргументы nowait и skip_locked. MySQL и MariaDB не поддерживают аргумент of.

Передача nowait=True, skip_locked=True, или of в select_for_update() с помощью бэкэндов баз данных, которые не поддерживают эти опции, такие как MySQL, вызывает ошибку NotSupportedError. Это предотвращает непредвиденную блокировку кода.

Оценивание набора запросов с select_for_update() в режиме автоматического подтверждения на бэкэндах, которые поддерживают SELECT ... FOR UPDATE, является ошибкой TransactionManagementError, потому что в этом случае строки не блокируются. Если это разрешено, это может привести к порче данных и может легко быть вызвано кодом, который ожидает выполнения вне транзакции.

Использование select_for_update() на бэкэндах, которые не поддерживают SELECT ... FOR UPDATE (таких как SQLite), не повлияет. SELECT ... FOR UPDATE не будет добавлен в запрос, и ошибка не возникает, если select_for_update() используется в режиме автоматического подтверждения.

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

Хотя select_for_update() обычно завершается неудачей в режиме автоматического подтверждения, поскольку TestCase автоматически оборачивает каждый тест в транзакцию, вызов select_for_update() в TestCase даже за пределами блока atomic() пройдет (возможно, неожиданно) без вывода ошибки TransactionManagementError. Для правильного тестирования select_for_update() следует использовать TransactionTestCase.

Некоторые выражения могут не поддерживаться

PostgreSQL не поддерживает select_for_update() с выражениями Window.

raw()

raw(raw_query, params=None, translations=None)

Принимает сырой SQL-запрос, выполняет его и возвращает экземпляр django.db.models.query.RawQuerySet. Этот экземпляр RawQuerySet можно перебирать так же, как обычный экземпляр QuerySet, чтобы получить объекты.

Дополнительную информацию см. в разделе Выполнение сырых SQL-запросов.

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

raw() всегда запускает новый запрос и не учитывает предыдущие фильтры. Поэтому его обычно следует вызывать из Manager или из нового экземпляра QuerySet.

Операторы, которые возвращают новые QuerySet

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

И (&)

Объединяет два набора запросов QuerySet с помощью оператора SQL AND.

Следующие выражения эквивалентны:

Model.objects.filter(x=1) & Model.objects.filter(y=2)
Model.objects.filter(x=1, y=2)
from django.db.models import Q
Model.objects.filter(Q(x=1) & Q(y=2))

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

SELECT ... WHERE x=1 AND y=2

ИЛИ (|)

Объединяет два набора запросов QuerySet с помощью оператора SQL OR.

Следующие выражения эквивалентны:

Model.objects.filter(x=1) | Model.objects.filter(y=2)
from django.db.models import Q
Model.objects.filter(Q(x=1) | Q(y=2))

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

SELECT ... WHERE x=1 OR y=2

Методы, которые не возвращают QuerySet

Следующие методы QuerySet оценивают QuerySet и возвращают значение, отличное от набора запросов QuerySet.

Эти методы не используют кэш (см. Кэширование и наборы запросов). Вместо этого они запрашивают базу данных каждый раз при вызове.

get()

get(**kwargs)

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

Entry.objects.get(id=1)
Entry.objects.get(blog=blog, entry_number=1)

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

Entry.objects.filter(pk=1).get()

Если get() не найдёт объект, он вызывает исключение Model.DoesNotExist:

Entry.objects.get(id=-999) # raises Entry.DoesNotExist

Если get() найдёт более одного объекта, он вызывает исключение Model.MultipleObjectsReturned:

Entry.objects.get(name='A Duplicated Name') # raises Entry.MultipleObjectsReturned

Оба этих класса исключений являются атрибутами класса модели и специфичны для этой модели. Если вы хотите обрабатывать такие исключения из нескольких вызовов get() для разных моделей, вы можете использовать их общие базовые классы. Например, вы можете использовать django.core.exceptions.ObjectDoesNotExist для обработки исключений DoesNotExist из нескольких моделей:

from django.core.exceptions import ObjectDoesNotExist

try:
    blog = Blog.objects.get(id=1)
    entry = Entry.objects.get(blog=blog, entry_number=1)
except ObjectDoesNotExist:
    print("Either the blog or entry doesn't exist.")

create()

create(**kwargs)

Удобный метод для создания объекта и сохранения его в одном шаге. Таким образом:

p = Person.objects.create(first_name="Bruce", last_name="Springsteen")

и:

p = Person(first_name="Bruce", last_name="Springsteen")
p.save(force_insert=True)

эквивалентны.

Параметр force_insert документирован в другом месте, но всё, что он означает, это то, что новый объект всегда будет создан. Обычно вам не нужно беспокоиться об этом. Однако, если ваша модель содержит значение первичного ключа, которое вы устанавливаете вручную, и если это значение уже существует в базе данных, вызов create() завершится ошибкой IntegrityError, так как первичные ключи должны быть уникальными. Будьте готовы обработать исключение, если вы используете ручные первичные ключи.

get_or_create()

get_or_create(defaults=None, **kwargs)

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

Возвращает кортеж из (object, created), где object — полученный или созданный объект, а created — булево значение, указывающее, был ли создан новый объект.

Это призвано предотвратить создание дублирующих объектов при одновременных запросах и как сокращение громоздкого кода. Например:

try:
    obj = Person.objects.get(first_name='John', last_name='Lennon')
except Person.DoesNotExist:
    obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
    obj.save()

Здесь, при одновременных запросах, могут быть предприняты несколько попыток сохранить Person с одинаковыми параметрами. Чтобы избежать этой гонки, приведенный выше пример может быть переписан с использованием get_or_create() следующим образом:

obj, created = Person.objects.get_or_create(
    first_name='John',
    last_name='Lennon',
    defaults={'birthday': date(1940, 10, 9)},
)

Любые ключевые аргументы, передаваемые в get_or_create() — кроме необязательного, называемого defaults — будут использоваться в вызове get(). Если объект найден, get_or_create() возвращает кортеж этого объекта и False.

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

Этот метод является атомарным, предполагая, что база данных обеспечивает уникальность ключевых аргументов (см. unique или unique_together). Если поля, используемые в ключевых аргументах, не имеют ограничения на уникальность, одновременные вызовы этого метода могут привести к вставке нескольких строк с одинаковыми параметрами.

Вы можете указать более сложные условия для полученного объекта, объединив get_or_create() с filter() и используя Q objects. Например, чтобы получить Роберта или Боба Марли, если любой из них существует, и создать последнего в противном случае:

from django.db.models import Q

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

update_or_create()

update_or_create(defaults=None, **kwargs)

Удобный метод для обновления объекта с заданными kwargs, создавая новый, если необходимо. defaults — это словарь пар (поле, значение), используемых для обновления объекта. Значения в defaults могут быть вызовами функций.

Возвращает кортеж (object, created), где object — созданный или обновлённый объект, а created — булево значение, указывающее, был ли создан новый объект.

Метод update_or_create пытается извлечь объект из базы данных на основе заданных kwargs. Если совпадение найдено, он обновляет поля, переданные в словаре defaults.

Это предназначено как сокращение для шаблонного кода. Например:

defaults = {'first_name': 'Bob'}
try:
    obj = Person.objects.get(first_name='John', last_name='Lennon')
    for key, value in defaults.items():
        setattr(obj, key, value)
    obj.save()
except Person.DoesNotExist:
    new_values = {'first_name': 'John', 'last_name': 'Lennon'}
    new_values.update(defaults)
    obj = Person(**new_values)
    obj.save()

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

obj, created = Person.objects.update_or_create(
    first_name='John', last_name='Lennon',
    defaults={'first_name': 'Bob'},
)

Для подробного описания того, как разрешаются имена, переданные в kwargs, см. get_or_create().

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

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

bulk_create()

bulk_create(objs, batch_size=None, ignore_conflicts=False)

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

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

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

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

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

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

В MySQL и MariaDB, установка параметра ignore_conflicts в True преобразует определенные типы ошибок, помимо дублирования ключа, в предупреждения. Даже в режиме Строго. Например: неверные значения или нарушения непустых полей. Для получения более подробной информации см. документацию MySQL и документацию MariaDB.

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

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

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

Поскольку сортировка делегируется базе данных, результаты по полям, допускающим нулевые значения, могут сортироваться по-разному при использовании разных баз данных. Например, PostgreSQL и MySQL сортируют нулевые значения, как если бы они были больше, чем ненулевые, в то время как SQLite делает наоборот.

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

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 для всех удалённых объектов (включая каскадные удаления).

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

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

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

as_manager()

classmethod as_manager()

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

explain()

explain(format=None, **options)

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

Например, при использовании PostgreSQL:

>>> print(Blog.objects.filter(title='My Blog').explain())
Seq Scan on blog  (cost=0.00..35.50 rows=10 width=12)
  Filter: (title = 'My Blog'::bpchar)

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

explain() поддерживается всеми встроенными базами данных, кроме Oracle, потому что реализация там нетривиальна.

Параметр format изменяет формат вывода от значения по умолчанию базы данных, обычно текстового. PostgreSQL поддерживает 'TEXT', 'JSON', 'YAML', и 'XML'. 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

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

Примеры:

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

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

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

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

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

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

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

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

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

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

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

Учитывая производительность

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

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

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

gt

Больше, чем.

Пример:

Entry.objects.filter(id__gt=4)

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

SELECT ... WHERE id > 4;

gte

Больше или равно.

lt

Меньше, чем.

lte

Меньше или равно.

startswith

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

Пример:

Entry.objects.filter(headline__startswith='Lennon')

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

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

SQLite не поддерживает операторы сопоставления с учетом регистра LIKE; оператор startswith действует как istartswith для SQLite.

istartswith

Сопоставление без учета регистра, начинающееся с.

Пример:

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

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

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

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

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

endswith

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

Пример:

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

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

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

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

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

iendswith

Сопоставление без учета регистра, заканчивающееся на.

Пример:

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

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

SELECT ... WHERE headline ILIKE '%Lennon'

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

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

range

Тест диапазона (включительно).

Пример:

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

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

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

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

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

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

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

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

date

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

Пример:

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

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

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

year

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

Пример:

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

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

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

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

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

iso_year

Новое в Django 2.2.

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

Пример:

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

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

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

month

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

Пример:

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

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

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

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

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

day

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

Пример:

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

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

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

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

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

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

week

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

Пример:

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

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

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

week_day

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

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

Пример:

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

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

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

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

quarter

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

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

Entry.objects.filter(pub_date__quarter=2)

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

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

time

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

Пример:

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

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

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

hour

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

Пример:

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

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

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

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

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

minute

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

Пример:

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

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

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

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

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

second

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

Пример:

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

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

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

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

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

isnull

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

Пример:

Entry.objects.filter(pub_date__isnull=True)

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

SELECT ... WHERE pub_date IS NULL;

regex

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

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

Пример:

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

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

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

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

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

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

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

iregex

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

Пример:

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

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

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

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

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

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

Функции агрегации

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

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

SQLite не может обрабатывать агрегацию по полям даты/времени напрямую. Это связано с тем, что в SQLite нет встроенных полей даты/времени, и Django в настоящее время эмулирует эти функции, используя текстовое поле. Попытки использовать агрегацию по полям даты/времени в SQLite приведут к ошибке 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, distinct=False, filter=None, **extra)

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

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

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

distinct

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

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

Была добавлена поддержка distinct=True.

Count

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

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

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

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

distinct

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

Max

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

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

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

Min

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

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

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

StdDev

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

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

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

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

sample

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

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

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

Sum

class Sum(expression, output_field=None, distinct=False, filter=None, **extra)

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

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

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

distinct

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

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

Была добавлена поддержка distinct=True.

Variance

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

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

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

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

sample

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

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

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

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

В этом разделе представлена справочная информация по инструментам, связанным с запросами, которые не задокументированы в другом месте.

Q() объекты

class Q

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

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

Prefetch() объекты

class Prefetch(lookup, queryset=None, to_attr=None)

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

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

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

Аргумент queryset предоставляет базовый QuerySet для данного запроса. Это полезно для дальнейшей фильтрации операции предварительной выборки или для вызова select_related() из предварительно выбранного отношения, тем самым еще больше уменьшая количество запросов:

>>> voted_choices = Choice.objects.filter(votes__gt=0)
>>> voted_choices
<QuerySet [<Choice: The sky>]>
>>> prefetch = Prefetch('choice_set', queryset=voted_choices)
>>> Question.objects.prefetch_related(prefetch).get().choice_set.all()
<QuerySet [<Choice: The sky>]>

Аргумент to_attr задает результат операции предварительной выборки в пользовательском атрибуте:

>>> prefetch = Prefetch('choice_set', queryset=voted_choices, to_attr='voted_choices')
>>> Question.objects.prefetch_related(prefetch).get().voted_choices
[<Choice: The sky>]
>>> Question.objects.prefetch_related(prefetch).get().choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

Примечание

При использовании to_attr результат предварительной выборки хранится в списке. Это может значительно ускорить работу по сравнению с традиционными вызовами prefetch_related , которые хранят кэшированный результат внутри экземпляра QuerySet.

prefetch_related_objects()

prefetch_related_objects(model_instances, *related_lookups)

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

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

condition

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

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

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

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

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

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

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

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

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

    >>> Restaurant.objects.annotate(
    ...    pizzas_with_toppings_startswith_n=FilteredRelation(
    ...        'pizzas__toppings',
    ...        condition=Q(pizzas__toppings__name__startswith='n'),
    ...    ),
    ... )
    Traceback (most recent call last):
    ...
    ValueError: FilteredRelation's condition doesn't support nested relations (got 'pizzas__toppings__name__startswith').
    
  • QuerySet.only() и prefetch_related().
  • Наследованное GenericForeignKey от родительской модели.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/3.0/ref/models/querysets/

Spec-Zone.ru

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