2009-09-07 2 views
104

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

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

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

ответ

101
if not orgs: 
    # Do this... 
else: 
    # Do that... 
+3

Этот представляется предпочтительным и в документации, например: https: // docs.djangoproject.com/en/1.8/topics/http/shortcuts/#id7 – Wtower

+0

@Wtower Код, на который вы ссылаетесь, имеет контракт, чтобы поднять 404, если выражение фильтрации не попало ни в какие записи или для создания «списка» результата, если есть записи. Код будет попадать в базу данных только один раз. Если они использовали 'exist()' или 'count()' для того, чтобы сначала проверить, будут ли возвращены записи, они дважды бьют базу данных (один раз, чтобы проверить, один раз, чтобы получить записи). Это конкретная ситуация. Это не связано с тем, что в * общем случае * предпочтительный метод, чтобы узнать, будет ли запрос возвращать записи, следует использовать do 'if queryset: ...' – Louis

+1

@Louis код, на который я ссылаюсь, является только примером, который он содержит строка 'if not my_objects:', чтобы продемонстрировать, что так они делают это в документах. Все остальное совершенно не имеет значения, поэтому я не понимаю. Они могли бы также сделать тысячу запросов, и это все равно было бы совершенно неактуальным, поскольку это не вопрос этого ответа, с которым я четко заявляю, что согласен. – Wtower

3

Наиболее эффективным способом (до Джанго 1.2) заключается в следующем:

if orgs.count() == 0: 
    # no results 
else: 
    # alrigh! let's continue... 
+3

.exists() кажется еще более эффективным – dzida

+3

За исключением того, что через несколько месяцев после моего комментария был добавлен файл .exists(), а Django 1.2 (который включил этот API) был выпущен ~ 8 месяцев спустя. Но спасибо за нисходящее голосование и не потрудились проверить факты. – Bartosz

+4

Извините, я добавил небольшое изменение в ваш ответ, чтобы сделать его более точным и проголосовал положительно. – dzida

14

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

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

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

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

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

134

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

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

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

if orgs: 
    ... 

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

5

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

if not orgs: 

Это должно быть

if not orgs.count(): 

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

+4

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

9

Чтобы проверить пустоту QuerySet:

if orgs.exists(): 
    # Do something 

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

if orgs.first(): 
    # Do something 
+1

'if orgs.exists()' был покрыт [ответом] (http://stackoverflow.com/a/2373793/1906307), который был предоставлен примерно за 5 лет до этого. Единственное, что этот ответ приводит к таблице, которая * возможно, новая - 'if orgs.first()'. (Даже это спорно: он существенно отличается от делать 'Orgs [0]' [предложил] (http://stackoverflow.com/a/2098092/1906307) около 5 лет назад тоже) Вы должны развивать эту часть ответа: когда вы захотите сделать это ** вместо ** других предложенных ранее решений? – Louis