Перевірка порожнього набору запитів у 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. Більшість прикладів у документах просто ігнорує випадок, коли нічого не знайдено ...

Відповіді:


207
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
@Louis код, на який я посилаюся, є лише прикладом того, що він містить рядок, if not my_objects:щоб продемонструвати, що саме так вони роблять у документах. Все інше вкрай не має значення, тому я не розумію вашої точки зору. Вони також могли б зробити тисячу запитів, і це все одно було б абсолютно неважливо, оскільки це не сенс цієї відповіді, з чим я даю зрозуміти, що я згоден.
Wtower

1
@Wtower Це лише пояснення того, як get_object_or_404працює, а не вподобаний спосіб перевірити наявність будь-яких елементів у наборі запитів. Список Doing list () набір запитів отримає всі об'єкти набору запитів, що було б гірше, ніж запит двічі, якщо повернуто багато рядків.
minmaxavg

1
Більш детальну відповідь дивіться на відповідь @ leonid-shvechikov нижче: використання .exists()більш ефективне, якщо q не буде оцінюватися.
відвід

191

Починаючи з версії 1.2, Django має QuerySet. існує () метод, який є найбільш ефективним:

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

Але якщо ви все одно будете оцінювати QuerySet, краще скористатися:

if orgs:
   ...

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


.exists () призначений лише для .filter (), чи є щось для .get ()?
ролик

.getне повертає набір запитів. Він повертає об'єкт. Так що Google для цього
Асім

Це помітно ефективніше, якщо у вас є великий 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 orgsє 400+ мс і orgs.count()становить 250 мс. У моїх найпоширеніших випадках використання (у тих випадках, де є результати), ця методика часто призводить до зниження до 20 мс. (Я знайшов один випадок, це було 6).

Звичайно, це може бути набагато довше, залежно від того, наскільки базується база даних, щоб знайти результат. Або навіть швидше, якщо він швидко знайде його; YMMV.

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


10

Щоб перевірити порожнечу набору запитів:

if orgs.exists():
    # Do something

або ви можете перевірити перший елемент у наборі запитів, якщо його не існує, він повернеться None:

if orgs.first():
    # Do something

7
if orgs.exists()був охоплений відповіддю, який був наданий приблизно за 5 років до цього. Єдине, що ця відповідь подає до столу, можливо, нового if orgs.first(). (Навіть це є дискусійним: чи суттєво він відрізняється від orgs[0] запропонованого приблизно 5 років тому?) Вам слід було б розробити ту частину відповіді: коли ви хочете зробити це замість інших запропонованих раніше рішень?
Луї

9

Найефективніший спосіб (до джанго 1.2):

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

5
.exists () здається ще ефективнішим
dzida

5
За винятком того, що .exists () був доданий через кілька місяців після мого коментаря, і Django 1.2 (який включав цей API) був випущений ~ 8 місяців пізніше. Але дякую за те, що голосували за недію та не намагалися перевіряти факти.
Бартош

4
Вибачте, я додав невелику редагування до вашої відповіді, щоб зробити її більш точною і проголосував позитивно.
дзида

4

Я не згоден з присудком

if not orgs:

Вона повинна бути

if not orgs.count():

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


6
__nonzero__ вже перевантажений у QuerySet. Якщо результат не кешований (він ніколи не використовується при першому використанні набору запитів), поведінка __nonzero__ полягає в перегляді всіх елементів набору запитів. Це дуже погано, якщо набір великий.
hedleyroos

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.