Memeriksa queryset kosong di Django

183

Apa idiom yang disarankan untuk memeriksa apakah permintaan mengembalikan hasil?
Contoh:

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

Saya kira ada beberapa cara berbeda untuk memeriksa ini, tetapi saya ingin tahu bagaimana seorang pengguna Django yang berpengalaman akan melakukannya. Sebagian besar contoh dalam dokumen hanya mengabaikan kasus di mana tidak ada yang ditemukan ...

Niklas
sumber

Jawaban:

206
if not orgs:
    # Do this...
else:
    # Do that...
Adam
sumber
5
Ini tampaknya lebih disukai dalam dokumentasi juga, misalnya: docs.djangoproject.com/en/1.8/topics/http/shortcuts/#id7
Wtower
1
@ Power Kode yang Anda rujuk memiliki untuk kontrak untuk meningkatkan 404 jika ekspresi penyaringan tidak mencapai catatan apa pun atau untuk menghasilkan listhasil jika ada catatan. Kode di sana akan mengenai database hanya sekali. Jika mereka menggunakan exist()atau count()untuk pertama memeriksa apakah akan ada catatan yang dikembalikan, mereka akan memukul database dua kali (satu kali untuk memeriksa, satu kali untuk mendapatkan catatan). Ini adalah situasi khusus. Itu tidak berarti bahwa dalam kasus umum , metode yang lebih disukai untuk mengetahui apakah permintaan akan mengembalikan catatan adalah menggunakan doif queryset:...
Louis
1
@Louis kode yang saya rujuk hanyalah contoh yang berisi garis if not my_objects:untuk menunjukkan bahwa ini adalah cara mereka melakukannya dalam dokumen. Semua yang lain sama sekali tidak relevan jadi saya tidak mengerti maksud Anda. Mereka juga dapat membuat seribu pertanyaan dan itu masih akan sama sekali tidak relevan karena ini bukan poin dari jawaban ini, yang saya jelaskan dengan persetujuan saya.
Wtower
1
@Wtower Itu hanya penjelasan tentang cara get_object_or_404kerjanya, bukan cara yang disukai untuk memeriksa apakah ada elemen dalam queryset. Melakukan daftar () pada queryset akan mengambil setiap objek pada queryset, yang akan lebih buruk daripada meminta dua kali jika ada banyak baris yang dikembalikan.
minmaxavg
1
Untuk jawaban yang lebih rinci, lihat jawaban @ leonid-shvechikov di bawah ini: menggunakan .exists()lebih efisien jika qs tidak akan dievaluasi.
Persaingan
191

Sejak versi 1.2, Django memiliki QuerySet. ada () metode yang paling efisien:

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

Tetapi jika Anda akan mengevaluasi QuerySet, lebih baik menggunakan:

if orgs:
   ...

Untuk informasi lebih lanjut baca dokumentasi QuerySet.exists () .

Leonid Shvechikov
sumber
.exists () hanya untuk .filter (), apakah ada sesuatu untuk .get ()?
roll
.gettidak mengembalikan queryset. Ini mengembalikan suatu objek. Jadi google untuk itu
Aseem
Ini hanya terasa lebih efisien jika Anda memiliki QuerySet besar: docs.djangoproject.com/en/2.1/ref/models/querysets/#exists
Nathan Jones
16

Jika Anda memiliki banyak objek, ini bisa (kadang-kadang) jauh lebih cepat:

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

Pada proyek yang saya kerjakan dengan database besar, not orgs400+ ms dan orgs.count()250ms. Dalam kasus penggunaan saya yang paling umum (yang ada hasilnya), teknik ini sering menurunkannya hingga di bawah 20 ms. (Satu kasus yang saya temukan, itu 6.)

Bisa jadi lebih lama, tentu saja, tergantung pada seberapa jauh database harus mencari untuk menemukan hasilnya. Atau bahkan lebih cepat, jika ia menemukannya dengan cepat; YMMV.

EDIT: Ini akan sering lebih lambat daripada orgs.count()jika hasilnya tidak ditemukan, terutama jika kondisi yang Anda filter jarang terjadi; sebagai hasilnya, ini sangat berguna dalam fungsi tampilan di mana Anda perlu memastikan tampilan ada atau melempar Http404. (Di mana, orang akan berharap, orang-orang meminta URL yang ada lebih sering daripada tidak.)

Adam Playford
sumber
10

Untuk memeriksa kekosongan queryset:

if orgs.exists():
    # Do something

atau Anda dapat memeriksa item pertama dalam queryset, jika tidak ada item itu akan kembali None:

if orgs.first():
    # Do something
Tuss4
sumber
7
if orgs.exists()ditutupi oleh jawaban yang diberikan sekitar 5 tahun sebelum ini. Satu-satunya jawaban yang dibawa ke meja yang mungkin baru adalah if orgs.first(). (Bahkan ini masih bisa diperdebatkan: apakah ini jauh berbeda dengan melakukan yang orgs[0] disarankan sekitar 5 tahun yang lalu juga?) Anda harus mengembangkan bagian dari jawaban itu: kapan seseorang ingin melakukan ini daripada solusi lain yang diusulkan sebelumnya?
Louis
9

Cara paling efisien (sebelum Django 1.2) adalah ini:

if orgs.count() == 0:
    # no results
else:
    # alrigh! let's continue...
Bartosz
sumber
5
.exists () tampaknya lebih efisien
dzida
5
Kecuali bahwa .exists () ditambahkan beberapa bulan setelah komentar saya, dan Django 1.2 (yang memasukkan API) dirilis ~ 8 bulan kemudian. Namun terimakasih telah melakukan voting dan tidak repot untuk memeriksa fakta.
Bartosz
4
Maaf, saya menambahkan suntingan kecil ke jawaban Anda untuk membuatnya lebih akurat dan memberikan suara positif.
dzida
4

Saya tidak setuju dengan predikat itu

if not orgs:

Harus

if not orgs.count():

Saya mengalami masalah yang sama dengan hasil yang cukup besar (~ hasil 150k). Operator tidak kelebihan beban di QuerySet, jadi hasilnya sebenarnya dibongkar sebagai daftar sebelum pemeriksaan dilakukan. Dalam kasus saya waktu eksekusi turun tiga pesanan.

hedleyroos
sumber
6
__nonzero__ sudah kelebihan beban di QuerySet. Jika hasilnya tidak di-cache (itu tidak pernah pada penggunaan queryset pertama) perilaku __nonzero__ adalah untuk beralih pada semua elemen dalam queryset. Ini sangat buruk jika setnya besar.
hedleyroos
0

Anda juga bisa menggunakan ini:

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

Rupesh Chaudhari
sumber