Spec-Zone.ru › Django 1.8

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    entry_list = list(Entry.objects.all())
    
  • bool(). Проверка QuerySet в контексте булева значения, например, при использовании bool(), or, and или оператора if, вызовет выполнение запроса. Если существует хотя бы один результат, QuerySet является True, в противном случае False. Например:

    if Entry.objects.filter(headline="Test"):
       print("There is at least one Entry with the headline Test")
    

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

Сериализация QuerySets

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

Если вам нужно сериализовать только необходимую информацию для реконструкции QuerySet из базы данных впоследствии, сериализуйте атрибут query объекта QuerySet. Затем вы можете воссоздать исходный QuerySet (без загрузки результатов) с помощью кода примерно такого вида:

>>> import pickle
>>> query = pickle.loads(s)     # Assuming 's' is the pickled string.
>>> qs = MyModel.objects.all()
>>> qs.query = query            # Restore the original 'query'.

Атрибут query — это непрозрачный объект. Он представляет внутреннее состояние построения запроса и не является частью публичного API. Однако безопасно (и полностью поддерживается) сериализовать и десериализовать содержимое атрибута, как описано здесь.

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

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

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

API QuerySet

Вот формальное объявление 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. Проще говоря: если вам нужно спросить, вам не нужно его использовать.

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

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

filter

filter(**kwargs)

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

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

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

exclude

exclude(**kwargs)

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

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

Этот пример исключает все записи, дата которых pub_date позже 2005-1-3 И заголовок headline равен «Hello»:

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

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

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

Этот пример исключает все записи, дата которых pub_date позже 2005-1-3 ИЛИ заголовок

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline='Hello')
равен «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')

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

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

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

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

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

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

Добавлена сортировка по выражениям запроса.

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

Примечание

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

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

class Event(Model):
   parent = models.ForeignKey('self', 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 , если QuerySet был отсортирован каким-либо образом.

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

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

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

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

reverse

reverse()

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

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

my_queryset.reverse()[:5]

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

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

distinct

distinct(*fields)

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

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

Примечание

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

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

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

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

Примечание

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

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

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

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

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

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

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

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

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

Примечание

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

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

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

values

values(*fields)

Возвращает ValuesQuerySet — подкласс 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.

Последний пункт выше — новый. Ранее вызов only() и defer() после values() был разрешен, но либо вызывал ошибку, либо возвращал неправильные результаты.

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

Наконец, обратите внимание, что ValuesQuerySet — подкласс QuerySet, и он реализует большинство тех же методов. Вы можете вызвать filter() на нём, order_by(), и т. д. Это означает, что эти два вызова идентичны:

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(), оно вернёт все поля модели в порядке их объявления.

Обратите внимание, что этот метод возвращает ValuesListQuerySet. Этот класс ведёт себя как список. В большинстве случаев этого достаточно, но если вам нужен фактический объект списка Python, вы можете просто вызвать list() на нём, что вычислит queryset.

Часто требуется получить определённое значение поля конкретного экземпляра модели. Для этого используйте values_list() и последующий вызов get().

>>> Entry.objects.values_list('headline', flat=True).get(pk=1)
'First entry'

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)

Возвращает итератор, содержащий список объектов 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)

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

Следующие примеры иллюстрируют разницу между обычными и 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)

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

...то вызов 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').

Ранее последнее было эквивалентно select_related('bar').

prefetch_related

prefetch_related(*lookups)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • select

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

    Пример:

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

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

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

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

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

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

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

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

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

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

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

    Например, это сработает:

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

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

    До версии 1.8 вы не могли экранировать литерал %s.

  • where / tables

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

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

    Пример:

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

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

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

    Будьте внимательны при использовании параметра tables, если вы указываете таблицы, которые уже используются в запросе. При добавлении дополнительных таблиц через параметр tables, Django предполагает, что вы хотите включить эту таблицу ещё раз, если она уже включена. Это создаёт проблему, так как имя таблицы затем получит псевдоним. Если таблица появляется несколько раз в операторе SQL, второй и последующие появления должны использовать псевдонимы, чтобы база данных могла их различать. Если вы ссылаетесь на дополнительную таблицу, добавленную в параметр 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(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_psycopg2, oracle, и mysql баз данных поддерживают select_for_update(). Однако в MySQL нет поддержки аргумента nowait. Очевидно, пользователи внешних сторонних баз данных должны проверять документацию своего бэкенда на предмет специфики в таких случаях.

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

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

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

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

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

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

raw

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

raw был перемещен в класс QuerySet. Ранее он был только в Manager.

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

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

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

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

>>> 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() не делает этого.
  • Он не работает с множественными отношениями «многие ко многим».

Параметр 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() и используемые Django ORM). Использование 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(field_name=None)

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

первый

first()

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

Пример:

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

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

агрегировать

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

Возвращает 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(**kwargs)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

...делайте так:

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

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

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

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

удалить

delete()

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

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

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

# Delete all the entries belonging to this Blog.
>>> Entry.objects.filter(blog=b).delete()

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

blogs = Blog.objects.all()
# This will delete all Blogs and all of their Entry objects.
blogs.delete()

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

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

END_OF_DOCUMENT_MARKER

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

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

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

as_manager

classmethod as_manager()

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

Поиск по полям

Поиск по полям — это способ указать основную часть SQL-запроса. Они задаются в качестве аргументов ключевых слов для методов 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%')

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

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

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

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

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

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

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

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

gt

Больше чем.

Пример:

Entry.objects.filter(id__gt=4)

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

SELECT ... WHERE id > 4;

gte

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

lt

Меньше чем.

lte

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

startswith

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

Пример:

Entry.objects.filter(headline__startswith='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';

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

year

Для полей дат и дат-времени точное совпадение года. Принимает целое число года.

Пример:

Entry.objects.filter(pub_date__year=2005)

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

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

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

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

month

Для полей дат и дат-времени точное совпадение месяца. Принимает целое число от 1 (январь) до 12 (декабрь).

Пример:

Entry.objects.filter(pub_date__month=12)

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

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

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

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

день

Для полей date и datetime, точное совпадение дня. Принимает целое число - день.

Пример:

Entry.objects.filter(pub_date__day=3)

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

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

(Точный синтаксис SQL зависит от каждого движка базы данных.)

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

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

день недели

Для полей date и datetime, совпадение дня недели.

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

Пример:

Entry.objects.filter(pub_date__week_day=2)

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

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

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

час

Для полей datetime, точное совпадение часа. Принимает целое число от 0 до 23.

Пример:

Event.objects.filter(timestamp__hour=23)

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

SELECT ... WHERE EXTRACT('hour' FROM timestamp) = '23';

(Точный синтаксис SQL зависит от каждого движка базы данных.)

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

минута

Для полей datetime, точное совпадение минуты. Принимает целое число от 0 до 59.

Пример:

Event.objects.filter(timestamp__minute=29)

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

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

(Точный синтаксис SQL зависит от каждого движка базы данных.)

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

секунда

Для полей datetime, точное совпадение секунды. Принимает целое число от 0 до 59.

Пример:

Event.objects.filter(timestamp__second=31)

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

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

(Точный синтаксис SQL зависит от каждого движка базы данных.)

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

isnull

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

Пример:

Entry.objects.filter(pub_date__isnull=True)

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

SELECT ... WHERE pub_date IS NULL;

поиск

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

regex

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

Синтаксис регулярных выражений соответствует используемому бэкенду базы данных. В случае SQLite, который не поддерживает встроенные регулярные выражения, эта функция реализуется с помощью пользовательской функции REGEXP (на языке Python), и поэтому синтаксис регулярных выражений соответствует синтаксису модуля 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=None, **extra) [source]

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

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

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 для получения инструкций по получению и установке этого расширения.

Sum

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

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

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

Дисперсия

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

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

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

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

sample

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

SQLite

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

Классы, связанные с запросами

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

Q() объекты

class Q [source]

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

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

Prefetch() объекты

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

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

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

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

Spec-Zone.ru

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