как проверить queryset на то что он пустой django

В Django, какой самый эффективный способ проверить пустой набор запросов?

Я слышал предложения использовать следующее:

Скопировано из комментария ниже: «Я ищу инструкцию типа» В MySQL и PostgreSQL count() быстрее для коротких запросов, существует() быстрее для длинных запросов и использует QuerySet [0], когда это вероятно, что вам понадобится первый элемент, и вы хотите проверить, что он существует. Однако, когда count() работает быстрее, он только немного быстрее, поэтому всегда рекомендуется использовать exists() при выборе между ними.

ОТВЕТЫ

Ответ 1

Похоже, что qs.count() и qs.exists() эффективно эквивалентны. Поэтому я не нашел причины использовать exists() над count(). Последний не медленнее, и его можно использовать для проверки как существования, так и длины. Возможно, что оба метода exists() и count() оцениваются в одном и том же запросе в MySQL.

Используйте qs[0] только в том случае, если вам действительно нужен объект. Это значительно медленнее, если вы просто проверяете существование.

На Amazon SimpleDB, 400 000 строк:

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

Ответ 2

query.exists() является наиболее эффективным способом.

Особенно в postgres count() может быть очень дорого, иногда дороже, чем обычный запрос выбора.

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

qs[0] будет по-прежнему включать select_related, выбор полей и сортировку; так что это будет дороже.

Исходный код Django находится здесь (django/db/models/sql/query.py RawQuery.has_results):

Еще один вопрос, который получил меня на днях, вызывает QuerySet в выражении if. Это выполняет и возвращает весь запрос!

Если переменная query_set может быть None (аргумент unset для вашей функции), используйте:

Ответ 3

Это зависит от контекста использования.

Использовать QuerySet.count()

. если вам нужен только счет, а не делать len (queryset).

Использовать QuerySet.exists()

. если вы хотите узнать, существует ли хотя бы один результат, а не запрос.

Не злоупотребляйте count() и существует()

Если вам понадобятся другие данные из QuerySet, просто оцените его.

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

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

Ответ 4

@Решение Sam Odio было достойной отправной точкой, но есть несколько недостатков в методологии, а именно:

Поэтому вместо того, чтобы фильтровать что-то, что могло бы совпадать, я решил исключить то, что определенно не будет соответствовать, но, надеюсь, все равно избегать кеша БД, но также обеспечивает одинаковое количество строк.

Я тестировал только локальную базу данных MySQL с набором данных:

Итак, вы можете видеть, что count() примерно в 9 раз медленнее, чем exists() для этого набора данных.

[0] также работает быстро, но ему нужна обработка исключений.

Ответ 5

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

Источник

Проверка пустого набора запросов в Django

какова рекомендуемая идиома для проверки, возвращает ли запрос какие-либо результаты?
Пример:

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

7 ответов:

начиная с версии 1.2, Django имеет QuerySet.существует() метод, который является наиболее эффективным:

но если вы все равно собираетесь оценивать QuerySet, лучше использовать:

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

может быть гораздо дольше, конечно, в зависимости от того, как далеко база данных должна искать, чтобы найти результат. Или даже быстрее, если он его найдет быстро; YMMV.

редактировать: этот будет часто быть медленнее, чем orgs.count() если результат не найден, особенно если условие, которое вы фильтруете, является редким; в результате это особенно полезно в функциях представления, где вам нужно убедиться, что представление существует или бросить Http404. (Где, хотелось бы надеяться, люди просят URL-адреса, которые существуют чаще, чем нет.)

чтобы проверить пустоту queryset:

или вы можете проверить первый элемент в queryset, если он не существует, он вернет None :

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

150k результатов). Оператор не перегружается в QuerySet,поэтому результат фактически распаковывается в виде списка перед проверкой. В моем случае время исполнения сократилось на три ордера.

самый эффективный способ (до django 1.2) это:

Источник

Документация Django 1.9

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

В примерах будут использованы примеры моделей web-блога представленные в разделе о выполнении запросов.

Когда вычисляется QuerySets¶

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

QuerySet будет вычислен при таких действиях:

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

Pickling/кэширование. Смотрите соответствующий раздел о pickling QuerySets. Основное замечание это то, что при этих операциях будет выполнен запрос к базе данных.

len(). QuerySet будет вычислен при выполнении len() над ним. Как вы и ожидаете будет возвращено количество объектов в результате выборки.

list(). QuerySet будет вычислен при использовании list() над ним. Например:

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

Атрибут query не является частью публичного API, и является частью внутреннего механизма создания запросов. Однако, поддерживает использование pickle и unpickle как показано в примере выше.

нельзя переносить “pickles” между версиями

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

QuerySet API¶

Вот документированное объявление QuerySet :

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

Класс QuerySet имеет два публичных атрибута:

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

Методы, которые возвращают новый QuerySets¶

filter¶

Возвращает новый QuerySet содержащий объекты отвечающие параметрам фильтрации.

exclude¶

Возвращает новый QuerySet содержащий объекты не отвечающие параметрам фильтрации.

Этот пример исключает все записи с pub_date раньше 3.01.2005 И с headline равным “Hello”:

Это эквивалентно запросу SQL:

Этот пример исключает все записи с pub_date раньше 3.01.2005 ИЛИ с headline равным “Hello”:

Это эквивалентно запросу SQL:

Обратите внимание на второй пример, который больше ограничивает выборку.

annotate¶

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

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

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

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

Для углубленного изучения агрегации смотрите раздел про агрегацию.

order_by¶

Заметка: запрос с order_by(‘?’) может быть медленным и сильно нагружать базу данных, зависит от типа базы данных, которую вы используете.

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

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

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

Была добавлена возможность сортировать по выражению.

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

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

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

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

reverse¶

Чтобы получить “последние” пять объектов выполните:

distinct¶

Возвращает QuerySet с добавленным SELECT DISTINCT в SQL запрос. Повторяющиеся записи будут исключены из результатов запроса.

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

values¶

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

Этот пример показывает разницу между результатом возвращаемым values() и объектами модели:

Следует упомянуть несколько тонкостей:

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

Разработчики Django предпочитают использовать в первую очередь методы влияющие на SQL-запрос, далее методы влияющие на вывод данных (такие как values() ), хотя это и не имеет значения. Это ваш шанс проявить индивидуальность.

values_list¶

Если вы указали больше одного поля, использование flat будет ошибкой.

Распространенная задача получить значение поля из определенной записи. Для этого используйте values_list() и get() :

dates¶

«year» возвращает список уникальных значений года из всех дат указанного поля.

«month» возвращает список уникальных значений года/месяца из всех дат указанного поля.

«day» возвращает список уникальных значений года/месяца/дня из всех дат указанного поля.

datetimes¶

SQLite: установите pytz — преобразование выполняется в Python.

PostgreSQL: нет дополнительных требований (смотрите Time Zones).

Oracle: нет дополнительных требований (смотрите Choosing a Time Zone File).

MySQL: установите pytz и загрузите таблицы часовых поясов с помощью mysql_tzinfo_to_sql.

select_related¶

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

И с select_related :

Вы можете использовать select_related() с любым “queryset”:

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

Можно указывать поля связанных моделей как и при запросе. Например, у нас есть такие модели:

. тогда вызов Book.objects.select_related(‘person__hometown’).get(id=4) получит данные связанных Person и связанных City :

prefetch_related¶

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

и выполняется такой код:

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

Запрос для загрузки данных, указанных в prefetch_related() будет выполнен после выполнения основного запроса.

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

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

Можно использовать такой запрос:

Это вернет “best pizza” и все “toppings” для них для каждого ресторана. Будет выполнено 3 запроса.

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

Использование нескольких вызовов prefetch_related соберет вместе все предварительно загружаемые поля. Чтобы их обнулить, вызовите метод prefetch_related с аргументом None :

prefetch_related в основном использует оператор ‘IN’ SQL запроса. Это означает, что для больших QuerySet может быть создано сложное условие ‘IN’, что, в зависимости от базы данных, может привести к проблемам с производительностью при разборе и выполнении SQL запроса. Всегда анализируйте(profile) ваш запрос!

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

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

В самом простом варианте Prefetch аналогичен обычному строковому аргументу:

Результат с собственным to_attr может использоваться в аргументах запроса:

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

Вы хотите загрузить только часть связанных объектов.

Вы хотите оптимизировать запрос, отфильтровав часть полей :

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

Возьмем следующие примеры:

В это примере ‘pizzas__toppings’ уже получает всю необходимую информацию, поэтому ‘pizzas’ лишний. (FIXME)

extra¶

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

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

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

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

Полученный SQL запрос выглядит таким образом:

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

. будет переведено (примерно) в следующий SQL:

Будьте внимательны при добавлении в параметр tables таблиц, которые уже используются запросом. В таком случае Django предполагает, что вы хотите добавить их повторно. Это создает проблему, т.к. таблица будет добавлена с псевдонимом(an alias). Если таблица несколько раз используется в запросе, второй и последующие вхождения должны использовать псевдонимы, чтобы база данных могла различить их. При обращении к добавленной таблице в параметре where вы получите ошибку.

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

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

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

defer¶

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

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

Результат все также будет содержать объекты модели. Каждое не выбранное поле будет получено из базы данных при обращении к нему (одна за раз, не все “отложенные” поля сразу).

Порядок добавления полей не имеет значения. Вызов defer() с полем, которое уже было добавлено в список “отложенных”, ничего не изменит (поле все также не будет выбираться из базы данных).

Вы можете указать поля связанных моделей (если эти модели загружаются через select_related() ) используя стандартный синтаксис двух нижних подчеркиваний для разделения полей:

Если вы хотите очистить список “отложенных” полей, передайте None как параметр для defer() :

Метод defer() (и его “коллега” only() ) предназначены только для опытных пользователей. Они предоставляют возможность оптимизировать запрос. Но для начала вам следует проанализировать его, точно определить какие данные вам необходимы и удостовериться, что разница между получением всех полей и получением определенных, будет значительной.

Например, обе эти модели используют одну таблицу в базе данных:

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

При вызове only() будет заменено множество загружаемых полей. Название метода говорит само за себя: только эти поля должны быть загружены; все остальные – “отложены”. Таким образом при последовательном вызове only() несколько раз, только поля из последнего вызова будут загружены:

using¶

select_for_update¶

Все, удовлетворяющие фильтрам, строки будут заблокированы до завершения транзакции, то есть другие транзакции не смогут изменить или заблокировать это строки.

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

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

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

create¶

Удобный метод чтобы создать и сохранить объект. Таким образом:

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

get_or_create¶

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

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

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

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

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

Возьмем следующие модели:

Это произошло, потому что мы пытались получить или создать “Chapter 1” для книги “Ulysses”, но ни один объект не был найден, т.к. он не связан с этой книгой, и мы получили ошибку при попытке его создать т.к. поле title должно быть уникальным.

update_or_create¶

Удобный метод для обновления объекта по заданным параметрам поиска kwargs и создания нового при необходимости. defaults – словарь полей и значений для обновления объекта.

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

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

bulk_create¶

Этот метод позволяет сохранить в базе данных множество объектов одним запросом:

Следует упомянуть ряд оговорок:

Метод модели save() не будет вызван, и сигналы pre_save и post_save не будут вызваны.

Не работает с дочерними моделями при multi-table наследовании.

Не работает со связями многое-ко-многим.

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

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

count¶

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

in_bulk¶

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

При передаче в in_bulk() пустого списка будет получен пустой словарь.

iterator¶

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

latest¶

Этот пример возвращает последний объект Entry в таблице по полю pub_date :

Заметим что earliest() и latest() существует исключительно для удобства и читаемости.

earliest¶

Работает как и latest() только наоборот.

first¶

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

first() создан просто для удобства и аналогичен следующему коду:

aggregate¶

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

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

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

Для углубленного изучения агрегации смотрите раздел про агрегацию.

exists¶

Самый эффективный способ определить принадлежит ли объект с уникальным полем (например, primary_key ) какому-либо QuerySet :

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

И для определения есть ли какой-либо объект в результате:

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

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

update¶

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

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

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

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

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

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

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

delete¶

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

Например, удалим все записи для определенного блога:

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

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

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

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

Внешние ключи со значением on_delete DO_NOTHING не мешают быстрому удалению.

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

as_manager¶

Операторы фильтрации¶

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

exact¶

iexact¶

contains¶

Регистрозависимая проверка на вхождение.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *