Spec-Zone.ru › Django 1.9

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

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

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

Когда оцениваются QuerySet

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

Вы можете оценить QuerySet следующими способами:

  • Итерация. QuerySet итерируемый, и он выполняет свой запрос к базе данных в первый раз при итерации по нему. Например, это выведет заголовок всех записей в базе данных:

    for e in Entry.objects.all():
        print(e.headline)
    

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

  • Разбиение на части (слайсинг). Как объясняется в Ограничение QuerySet, QuerySet можно разбить на части с использованием синтаксиса срезов Python. Разбиение на части невычисленного QuerySet обычно возвращает другой невычисленный QuerySet, но Django выполнит запрос к базе данных, если вы используете параметр «шаг» синтаксиса срезов, и вернет список. Разбиение на части уже вычисленного QuerySet также возвращает список.

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

  • Сериализация/кеширование. Подробности о процессах, связанных с сериализацией QuerySet, см. в следующем разделе. Важно, что результаты считываются из базы данных.
  • repr(). QuerySet оценивается, когда вы вызываете repr() на нем. Это сделано для удобства в интерактивном интерпретаторе Python, чтобы вы могли сразу видеть результаты при взаимодействии с API.
  • len(). QuerySet оценивается, когда вы вызываете len() на нем. Как можно догадаться, это возвращает длину списка результатов.

    Примечание: Если вам нужно только определить количество записей в наборе (и вам не нужны сами объекты), гораздо эффективнее обработать подсчет на уровне базы данных, используя SQL SELECT COUNT(*). Django предоставляет метод count() именно для этой цели.

  • list(). Вызов list() на QuerySet принудительно выполняет оценку. Например:

    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, когда вы пытаетесь десериализовать queryset в версии Django, отличной от той, в которой он был сериализован.

QuerySet API

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

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

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

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

ordered

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

db

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

Примечание

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

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

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

filter()

filter(**kwargs)

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

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

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

exclude()

exclude(**kwargs)

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

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

В этом примере исключаются все записи, дата публикации которых позже 2005-01-03 И заголовок которых «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')

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

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

Каждый аргумент к 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')

Также можно упорядочить набор запросов по связанному полю, не производя JOIN, указав _id связанного поля:

# No Join
Entry.objects.order_by('blog_id')

# Join
Entry.objects.order_by('blog__id')

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

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

Добавлена возможность упорядочивания по выражениям запроса.

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

Добавлена возможность упорядочивания по выражениям, таким как Lower.

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

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

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

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

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

reverse()

reverse()

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

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

my_queryset.reverse()[:5]

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

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

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

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

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

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

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

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

Пример:

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

Несколько нюансов, которые стоит упомянуть:

  • Если у вас есть поле, называемое foo, которое является ForeignKey, вызов values() по умолчанию вернёт ключ словаря foo_id, так как это имя скрытого атрибута модели, хранящего фактическое значение (атрибут foo относится к связанной модели). Когда вы вызываете values() и передаёте имена полей, вы можете передать либо foo, либо foo_id, и вы получите то же самое (ключ словаря будет соответствовать имени поля, которое вы передали).

    Например:

    >>> Entry.objects.values()
    [{'blog_id': 1, 'headline': 'First Entry', ...}, ...]
    
    >>> Entry.objects.values('blog')
    [{'blog': 1}, ...]
    
    >>> Entry.objects.values('blog_id')
    [{'blog_id': 1}, ...]
    
  • При использовании values() вместе с distinct(), будьте внимательны к влиянию сортировки на результаты. Смотрите примечание в distinct() для получения подробностей.
  • Если вы используете values() пункт после вызова extra(), любые поля, определённые аргументом select в extra(), должны быть явно включены в вызов values() . Любой вызов extra(), сделанный после вызова values(), проигнорирует дополнительные выбранные поля.
  • Вызов only() и defer() после values() не имеет смысла, поэтому это приведёт к исключению NotImplementedError.

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

Наконец, обратите внимание, что вы можете вызвать 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')
[{'name': 'My blog', 'entry__headline': 'An entry'},
     {'name': 'My blog', 'entry__headline': 'Another entry'}, ...]

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

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

values_list()

values_list(*fields, flat=False)

Это аналогично values(), но вместо возвращения словарей оно возвращает кортежи при итерации. Каждый кортеж содержит значение из соответствующего поля, переданного в вызов values_list() — поэтому первый элемент — первое поле и т.д. Например:

>>> Entry.objects.values_list('id', 'headline')
[(1, 'First entry'), ...]

Если вы передаёте только одно поле, вы также можете передать параметр flat. Если True, это будет означать, что возвращаемые результаты являются отдельными значениями, а не кортежами из одного элемента. Пример должен прояснить разницу:

>>> Entry.objects.values_list('id').order_by('id')
[(1,), (2,), (3,), ...]

>>> Entry.objects.values_list('id', flat=True).order_by('id')
[1, 2, 3, ...]

Ошибка передавать flat при наличии более одного поля.

Если вы не передаёте значения в 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')
[('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')
[('Noam Chomsky',), ('George Orwell',), (None,)]

dates()

dates(field, kind, order='ASC')

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

field должно быть именем поля DateField вашей модели. kind должно быть либо "year", либо "month", либо "day". Каждый объект datetime.date в списке результатов «обрезается» до указанного type.

  • "year" возвращает список всех различных значений года для поля.
  • "month" возвращает список всех различных значений год/месяц для поля.
  • "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', '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", либо "day", либо "hour", либо "minute", либо "second". Каждый объект datetime.datetime в списке результатов «обрезается» до указанного type.

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

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

Примечание

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

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

none()

none()

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

Примеры:

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

all()

all()

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

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

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:

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.

b = Book.objects.get(id=4) # No select_related() in this example.
p = b.author         # Hits the database.
c = p.hometown       # Hits the database.

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

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

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

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

>>> without_relations = queryset.select_related(None)

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

prefetch_related()

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):              # __unicode__ on Python 2
        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 начал оценку и был выполнен основной запрос.

END_OF_DOCUMENT_MARKER

Обратите внимание, что кэш результатов основного 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() — это новый и другой запрос. Кэш предварительной выборки здесь не поможет; на самом деле, он снижает производительность, так как вы выполнили запрос к базе данных, которым не воспользовались. Поэтому используйте эту функцию с осторожностью!

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

class Restaurant(models.Model):
    pizzas = models.ManyToManyField(Pizza, related_name='restaurants')
    best_pizza = models.ForeignKey(Pizza, related_name='championed_by')

Следующее является допустимым:

>>> 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, использующего оператор «В». Это означает, что для большого QuerySet может быть сгенерирована большая клауза «В», что в зависимости от базы данных может создавать собственные проблемы с производительностью при анализе или выполнении запроса SQL. Всегда проводите профилирование для вашего случая использования!

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

Вы можете использовать объект Prefetch для дальнейшего управления операцией предварительной загрузки.

В самом простом виде Prefetch эквивалентен традиционным поисковым запросам на основе строк:

>>> Restaurant.objects.prefetch_related(Prefetch('pizzas__toppings'))

Вы можете предоставить пользовательский набор результатов с необязательным аргументом queryset. Это можно использовать для изменения стандартного порядка сортировки набора результатов:

>>> Restaurant.objects.prefetch_related(
...     Prefetch('pizzas__toppings', queryset=Toppings.objects.order_by('name')))

Или вызвать select_related(), где это применимо, чтобы еще больше сократить количество запросов:

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

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

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

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

Запросы, созданные с помощью пользовательских to_attr, по-прежнему могут быть просмотрены как обычно другими запросами:

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

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

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

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

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

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

Примечание

Порядок запросов имеет значение.

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

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

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

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

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

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

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

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

extra()

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

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

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

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

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

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

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

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

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

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

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

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

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

END_OF_DOCUMENT_MARKER
  • select

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

    Пример:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    До версии 1.8, у вас не было возможности экранировать литерал %s.

  • where / tables

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

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

    Пример:

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

    ...приблизительно переводится на следующий SQL:

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

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

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

  • order_by

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

    Например:

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

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

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

Например:

entries = Entry.objects.select_for_update().filter(author=request.user)

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

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

В настоящее время postgresql, oracle, и mysql базы данных поддерживают select_for_update(). Однако MySQL не поддерживает аргумент nowait . Очевидно, пользователи внешних баз данных третьих сторон должны проверить документацию своей базы данных для получения спецификаций в этих случаях.

Передача nowait=True в select_for_update() с помощью баз данных, которые не поддерживают nowait, таких как MySQL, приведет к возникновению DatabaseError. Это сделано для предотвращения неожиданного блокирования кода.

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

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

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

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

raw()

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

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

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

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

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

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

Следующие методы QuerySet оценивают QuerySet и возвращают что-то кроме QuerySet.

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

get()

get(**kwargs)

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

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

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

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

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

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

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

Этот шаблон становится довольно громоздким по мере увеличения количества полей в модели. Приведённый выше пример можно переписать с использованием 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. Если найдено несколько объектов, get_or_create поднимает исключение MultipleObjectsReturned. Если объект не найден, get_or_create() создаст и сохранит новый объект, вернув кортеж из нового объекта и True. Новый объект будет создан примерно по следующему алгоритму:

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

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

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

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

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

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

Если вы используете MySQL, обязательно используйте уровень изоляции READ COMMITTED вместо REPEATABLE READ (по умолчанию), иначе могут возникнуть случаи, когда get_or_create поднимет исключение IntegrityError, но объект не появится в последующем вызове get().

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

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

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

Предположим следующие модели:

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

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

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

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

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

bulk_create()

bulk_create(objs, batch_size=None)

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

>>> Entry.objects.bulk_create([
...     Entry(headline="Django 1.0 Released"),
...     Entry(headline="Django 1.1 Announced"),
...     Entry(headline="Breaking: Django is awesome")
... ])

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

  • Метод save() модели не будет вызван, и сигналы pre_save и post_save не будут отправлены.
  • Он не работает с дочерними моделями в сценарии наследования с несколькими таблицами.
  • Если первичный ключ модели является AutoField, он не извлекает и не устанавливает атрибут первичного ключа, как save().
  • Он не работает с взаимосвязями многие ко многим.

Поддержка использования bulk_create() с прокси-моделями была добавлена.

Параметр batch_size управляет количеством объектов, создаваемых в одном запросе. По умолчанию все объекты создаются в одной группе, за исключением SQLite, где по умолчанию используется не более 999 переменных в запросе.

count()

count()

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

Пример:

# 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() будет быстрее).

В зависимости от используемой базы данных (например, PostgreSQL или MySQL), count() может вернуть большое целое число вместо обычного целого числа Python. Это особенность реализации, которая не должна создавать реальных проблем.

Обратите внимание, что если вам нужно количество элементов в QuerySet и вы также извлекаете экземпляры модели из него (например, итерируясь по нему), то, вероятно, эффективнее использовать len(queryset), которое не вызовет дополнительного запроса к базе данных, в отличие от count().

in_bulk()

in_bulk(id_list)

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

Пример:

>>> 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([])
{}

Если вы передадите in_bulk() пустой список, вы получите пустой словарь.

iterator()

iterator()

Выполняет запрос QuerySet и возвращает итератор (см. PEP 234) по результатам. Итератор QuerySet обычно кеширует свои результаты, чтобы повторные вычисления не приводили к дополнительным запросам. В отличие от iterator(), которое читает результаты напрямую без кэширования на уровне QuerySet (внутренне, итератор по умолчанию вызывает iterator() и кэширует возвращаемое значение). Для QuerySet, возвращающего большое количество объектов, к которым вам нужно получить доступ только один раз, это может привести к улучшению производительности и значительному сокращению потребления памяти.

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

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

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

Некоторые драйверы баз данных Python, такие как psycopg2, выполняют кэширование при использовании курсоров на стороне клиента (созданных с connection.cursor() и тем, что использует ORM Django). Использование iterator() не влияет на кэширование на уровне драйвера базы данных. Чтобы отключить это кэширование, обратитесь к курсорам на стороне сервера.

latest()

latest(field_name=None)

Возвращает последнюю запись в таблице по дате, используя указанное поле даты field_name.

Этот пример возвращает последнюю Entry в таблице, согласно полю pub_date.

Entry.objects.latest('pub_date')

Если в параметрах метаданных модели указано get_latest_by, вы можете опустить аргумент field_name в методе earliest() или latest(). Django по умолчанию будет использовать поле, указанное в get_latest_by.

Как и get(), методы earliest() и latest() генерируют исключение DoesNotExist, если объект с заданными параметрами не найден.

Обратите внимание, что earliest() и latest() существуют только для удобства и повышения читабельности.

earliest() и latest() могут возвращать экземпляры с датами NULL.

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

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

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

earliest()

earliest(field_name=None)

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

first()

first()

Возвращает первый объект, соответствующий запросу, или None если соответствующий объект не найден. Если в запросе нет сортировки, он автоматически сортируется по первичному ключу.

Пример:

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

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

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

last()

last()

Работает как first(), но возвращает последний объект в наборе запросов.

aggregate()

aggregate(*args, **kwargs)

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

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

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

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

>>> from django.db.models import Count
>>> q = Blog.objects.aggregate(Count('entry'))
{'entry__count': 16}

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

>>> q = Blog.objects.aggregate(number_of_entries=Count('entry'))
{'number_of_entries': 16}

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

exists()

exists()

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

exists() полезно для поиска, относящегося как к членству объекта в QuerySet, так и к существованию любых объектов в QuerySet, особенно в контексте большого QuerySet.

Наиболее эффективный метод определения, является ли модель с уникальным полем (например, primary_key) членом QuerySet, это:

entry = Entry.objects.get(pk=123)
if some_queryset.filter(pk=entry.pk).exists():
    print("Entry contained in queryset")

Что будет быстрее, чем следующее, требующее оценки и итерации по всему набору запросов:

if entry in some_queryset:
   print("Entry contained in QuerySet")

А также для поиска наличия элементов в наборе запросов:

if some_queryset.exists():
    print("There is at least one object in some_queryset")

Что будет быстрее, чем:

if some_queryset:
    print("There is at least one object in some_queryset")

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

Кроме того, если some_queryset еще не был оценен, но вы знаете, что он будет оценен в какой-то момент, то использование some_queryset.exists() потребует больше работы в целом (один запрос для проверки существования плюс дополнительный запрос для последующего извлечения результатов), чем просто использование bool(some_queryset), которое извлекает результаты, а затем проверяет, были ли они возвращены.

update()

update(**kwargs)

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

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

>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)

(Предполагается, что ваша модель Entry имеет поля pub_date и comments_on.)

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

>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False, headline='This is old')

Метод update() применяется мгновенно, и единственное ограничение для обновляемого QuerySet состоит в том, что он может обновлять только столбцы в основной таблице модели, а не в связанных моделях. Например, вы не можете сделать так:

>>> Entry.objects.update(blog__name='foo') # Won't work!

Однако фильтрация по связанным полям все же возможна:

>>> Entry.objects.filter(blog__id=1).update(comments_on=True)

Вы не можете вызвать update() для QuerySet, которому был применён срез или который по иным причинам не может быть отфильтрован.

Метод update() возвращает количество затронутых строк:

>>> Entry.objects.filter(id=64).update(comments_on=True)
1

>>> Entry.objects.filter(slug='nonexistent-slug').update(comments_on=True)
0

>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
132

Если вам нужно только обновить запись и вам не нужно работать с объектом модели, наиболее эффективным подходом является вызов update(), а не загрузка объекта модели в память. Например, вместо этого:

e = Entry.objects.get(id=10)
e.comments_on = False
e.save()

…сделайте так:

Entry.objects.filter(id=10).update(comments_on=False)

Использование update() также предотвращает гонку, при которой что-то может измениться в вашей базе данных в короткий промежуток времени между загрузкой объекта и вызовом save().

Наконец, помните, что update() выполняет обновление на уровне SQL и, следовательно, не вызывает никаких методов save() для ваших моделей, а также не генерирует сигналы pre_save или post_save (которые являются следствием вызова Model.save()). Если вы хотите обновить множество записей для модели, у которой есть пользовательский метод save(), пройдитесь по ним и вызовите save(), как показано ниже:

for e in Entry.objects.filter(pub_date__year=2010):
    e.comments_on = False
    e.save()

delete()

delete()

Выполняет запрос SQL DELETE для всех строк в QuerySet и возвращает количество удалённых объектов и словарь с количеством удалений по типу объекта.

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

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

>>> b = Blog.objects.get(pk=1)

# Delete all the entries belonging to this Blog.
>>> Entry.objects.filter(blog=b).delete()
(4, {'weblog.Entry': 2, 'weblog.Entry_authors': 2})

Добавлено возвращаемое значение, описывающее количество удалённых объектов.

По умолчанию, Django’s ForeignKey эмулирует SQL-ограничение ON DELETE CASCADE — другими словами, все объекты с внешними ключами, указывающими на объекты, которые будут удалены, будут удалены вместе с ними. Например:

>>> blogs = Blog.objects.all()

# This will delete all Blogs and all of their Entry objects.
>>> blogs.delete()
(5, {'weblog.Blog': 1, 'weblog.Entry': 2, 'weblog.Entry_authors': 2})

Это поведение каскадного удаления настраивается с помощью аргумента on_delete для ForeignKey.

Метод delete() выполняет массовое удаление и не вызывает никаких delete() методов для ваших моделей. Однако он отправляет сигналы pre_delete и post_delete для всех удалённых объектов (включая каскадные удаления).

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

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

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

as_manager()

classmethod as_manager()

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

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-бэкенда и Unicode-строк (не ASCII) следует учитывать примечание к базе данных о сравнении строк. SQLite не выполняет нечувствительное к регистру сопоставление для Unicode-строк.

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-бэкенда и Unicode-строк (не ASCII) следует учитывать примечание к базе данных о сравнении строк.

in

В заданном списке.

Пример:

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

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

SELECT ... WHERE id IN (1, 3, 4);

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

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

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

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

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

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

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

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

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

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

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

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

gt

Больше, чем.

Пример:

Entry.objects.filter(id__gt=4)

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

SELECT ... WHERE id > 4;

gte

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

lt

Меньше, чем.

lte

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

startswith

Регистрозависимое начинается с.

Пример:

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

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

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

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

istartswith

Нечувствительное к регистру начинается с.

Пример:

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

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

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

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

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

endswith

Регистрозависимое заканчивается на.

Пример:

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

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

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

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

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

iendswith

Нечувствительное к регистру заканчивается на.

Пример:

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

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

SELECT ... WHERE headline ILIKE '%will'

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

При использовании SQLite-бэкенда и Unicode-строк (не 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

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

Пример:

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

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

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

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

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

Допущена цепочка дополнительных поисков по полям.

month

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

Пример:

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

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

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

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

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

Допущена цепочка дополнительных поисков по полям.

day

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

Пример:

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

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

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

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

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

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

Допущена цепочка дополнительных поисков по полям.

week_day

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

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

Пример:

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

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

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

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

Допущена цепочка дополнительных поисков по полям.

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 варьируется для каждой базы данных.)

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

Добавлена поддержка TimeField в SQLite (в других базах данных поддерживается с версии 1.7).

Допущена цепочка дополнительных поисков по полям.

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 варьируется для каждой базы данных.)

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

Добавлена поддержка TimeField в SQLite (в других базах данных поддерживается с версии 1.7).

Допущена цепочка дополнительных поисков по полям.

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 варьируется для каждой базы данных.)

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

Добавлена поддержка TimeField в SQLite (в других базах данных поддерживается с версии 1.7).

Допущена цепочка дополнительных поисков по полям.

isnull

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

Пример:

Entry.objects.filter(pub_date__isnull=True)

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

SELECT ... WHERE pub_date IS NULL;

search

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

Пример:

Entry.objects.filter(headline__search="+Django -jazz Python")

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

SELECT ... WHERE MATCH(tablename, headline) AGAINST (+Django -jazz Python IN BOOLEAN MODE);

Обратите внимание, что это доступно только в MySQL и требует непосредственного изменения базы данных для добавления полнотекстового индекса. По умолчанию Django использует BOOLEAN MODE для полнотекстовых поисков. Дополнительные сведения см. в документации MySQL.

regex

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

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

Пример:

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

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

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

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

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

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

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

iregex

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

Пример:

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

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

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

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

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

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

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

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

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

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

Примечание

Функции агрегирования возвращают None при использовании с пустым QuerySet. Например, функция агрегирования Sum возвращает None вместо 0, если QuerySet не содержит записей. Исключением является Count, которая возвращает 0 , если QuerySet пуста.

У всех агрегатов есть следующие общие параметры:

expression

Строка, ссылающаяся на поле модели, или выражение запроса.

Функции агрегирования теперь могут ссылаться на несколько полей в сложных вычислениях.

output_field

Необязательный аргумент, представляющий поле модели возвращаемого значения

Аргумент output_field был добавлен.

Примечание

При объединении нескольких типов полей Django может определить output_field только если все поля одного типа. В противном случае вы должны предоставить output_field самостоятельно.

**extra

Ключевые аргументы, которые могут предоставить дополнительный контекст для SQL-запроса, сгенерированного агрегацией.

Avg

class Avg(expression, output_field=FloatField(), **extra) [source]

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

  • Значение по умолчанию: <field>__avg
  • Тип возвращаемого значения: float (или тип того, что output_field задано)

Параметр output_field был добавлен для агрегирования по нечисловым столбцам, таким как DurationField.

Count

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

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

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

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

distinct

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

Max

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

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

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

Min

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

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

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

StdDev

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

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

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

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

sample

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

SQLite

SQLite не предоставляет StdDev в стандартной комплектации. Реализация доступна в виде расширения для SQLite. Обратитесь к документации SQLite SQlite documentation за инструкциями по получению и установке этого расширения.

Sum

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

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

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

Variance

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

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

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

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

sample

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

SQLite

SQLite не предоставляет Variance в стандартной комплектации. Реализация доступна в виде расширения для SQLite. Обратитесь к документации SQLite SQlite documentation за инструкциями по получению и установке этого расширения.

Связанные с запросами классы

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

Q() объекты

class Q [source]

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

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

Prefetch() объекты

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

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

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

>>> Question.objects.prefetch_related(Prefetch('choice_set')).get().choice_set.all()
[<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()
[<Question: Question object>]

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

>>> voted_choices = Choice.objects.filter(votes__gt=0)
>>> voted_choices
[<Choice: The sky>]
>>> prefetch = Prefetch('choice_set', queryset=voted_choices)
>>> Question.objects.prefetch_related(prefetch).get().choice_set.all()
[<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()
[<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]

Примечание

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

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

Spec-Zone.ru

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