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

183

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

orgs = Organisation.objects.filter(name__iexact = 'Fjuk inc')
# If any results
    # Do this with the results without querying again.
# Else, do something else...

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

Никлас
источник

Ответы:

206
if not orgs:
    # Do this...
else:
    # Do that...
Адам
источник
5
Это также представляется предпочтительным в документации, например: docs.djangoproject.com/en/1.8/topics/http/shortcuts/#id7
Wtower
1
@Wtower Код, на который вы ссылаетесь, имеет для контракта значение 404, если выражение фильтрации не попадает ни в какие записи, или для получения listрезультата при наличии записей. Код там попадет в базу данных только один раз. Если бы они использовали exist()или count()сначала проверили, будут ли возвращены записи, они дважды попадут в базу данных (один раз, чтобы проверить, один раз, чтобы получить записи). Это конкретная ситуация. Это не означает, что в общем случае предпочтительным способом узнать, будет ли запрос возвращать записи, является использование doif queryset:...
Louis
1
@ Луис, на который я ссылаюсь, является лишь примером того, что он содержит строку, if not my_objects:демонстрирующую, что именно так они и делают в документах. Все остальное совершенно не имеет значения, поэтому я не понимаю вашу точку зрения. С таким же успехом они могли бы сделать тысячу запросов, и это все равно было бы совершенно неактуально, так как это не является темой этого ответа, с чем я ясно заявляю, что согласен.
Wtower
1
@Wtower Это просто объяснение того, как get_object_or_404работает, а не предпочтительный способ проверки наличия каких-либо элементов в наборе запросов . Выполнение list () для набора запросов приведет к извлечению всех объектов в наборе запросов, что было бы хуже, чем двойной запрос, если возвращено много строк.
minmaxavg
1
Для более подробного ответа взгляните на ответ @ leonid-shvechikov ниже: использование .exists()более эффективно, если qs не будет оцениваться.
Гиваль
191

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

if orgs.exists():
    # Do this...
else:
    # Do that...

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

if orgs:
   ...

Для получения дополнительной информации прочитайте документацию QuerySet.exists () .

Леонид Швечиков
источник
.exists () только для .filter (), есть что-то для .get ()?
рулет
.getне возвращает набор запросов. Возвращает объект. Так что погуглите
Aseem
Это только заметно эффективнее, если у вас большой QuerySet: docs.djangoproject.com/en/2.1/ref/models/querysets/#exists
Натан Джонс,
16

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

try:
    orgs[0]
    # If you get here, it exists...
except IndexError:
    # Doesn't exist!

В проекте, над которым я работаю, с огромной базой данных, not orgs400+ мс и orgs.count()250 мс . В моих наиболее распространенных случаях использования (в тех случаях, когда есть результаты), эта техника часто сводится к тому, что до 20 мс. (Один случай, который я обнаружил, был 6.)

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

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

Адам Плейфорд
источник
10

Чтобы проверить пустоту набора запросов:

if orgs.exists():
    # Do something

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

if orgs.first():
    # Do something
Tuss4
источник
7
if orgs.exists()был покрыт ответом, который был предоставлен примерно за 5 лет до этого. Единственное, что этот ответ приносит к столу, который, возможно, является новым, является if orgs.first(). (Даже это спорно: он существенно отличается от делать то orgs[0] предложило около 5 лет назад тоже?) Вы должны развивать эту часть ответа: когда бы один хочет сделать это вместо других предложенных решений ранее?
Луи
9

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

if orgs.count() == 0:
    # no results
else:
    # alrigh! let's continue...
Бартош
источник
5
.exists () кажется еще более эффективным
Дзида
5
За исключением того, что .exists () был добавлен через несколько месяцев после моего комментария, а Django 1.2 (который включал этот API) был выпущен ~ 8 месяцев спустя. Но спасибо за голосование и не удосужились проверить факты.
Бартош
4
Извините, я добавил небольшое изменение к вашему ответу, чтобы сделать его более точным и проголосовал положительно.
Дзида
4

Я не согласен с предикатом

if not orgs:

Так должно быть

if not orgs.count():

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

hedleyroos
источник
6
__nonzero__ уже перегружен в QuerySet. Если результат не кэшируется (он никогда не используется при первом использовании набора запросов), поведение __nonzero__ состоит в том, чтобы перебирать все элементы в наборе запросов. Это очень плохо, если набор большой.
hedleyroos
0

Вы также можете использовать это:

if(not(orgs)): #if orgs is empty else: #if orgs is not empty

Рупеш Чаудхари
источник