Saya sedang berdebat tentang hal ini dengan beberapa rekan. Apakah ada cara yang disukai untuk mengambil objek di Django ketika Anda hanya mengharapkan satu?
Dua cara yang jelas adalah:
try:
obj = MyModel.objects.get(id=1)
except MyModel.DoesNotExist:
# We have no object! Do something...
pass
Dan:
objs = MyModel.objects.filter(id=1)
if len(objs) == 1:
obj = objs[0]
else:
# We have no object! Do something...
pass
Metode pertama tampaknya lebih benar secara perilaku, tetapi menggunakan pengecualian dalam aliran kontrol yang mungkin menyebabkan beberapa overhead. Yang kedua lebih bundaran tetapi tidak akan pernah memunculkan pengecualian.
Adakah pemikiran yang lebih disukai? Mana yang lebih efisien?
QS.get()
bagus. 2. Detail penting: apakah "hanya mengharapkan satu" berarti selalu objek 0-1, atau dimungkinkan untuk memiliki 2+ objek dan kasing itu juga harus ditangani (dalam hal inilen(objs)
adalah ide yang buruk)? 3. Jangan berasumsi apa-apa tentang overhead tanpa patokan (saya pikir dalam hal initry/except
akan lebih cepat selama setidaknya setengah dari panggilan mengembalikan sesuatu)Anda dapat menginstal modul yang disebut django-annoying dan kemudian lakukan ini:
sumber
1 benar. Dalam Python pengecualian memiliki overhead yang sama dengan return. Untuk bukti disederhanakan Anda dapat melihat ini .
2 Inilah yang dilakukan Django di backend.
get
panggilanfilter
dan menimbulkan pengecualian jika tidak ada item ditemukan atau jika lebih dari satu objek ditemukan.sumber
try
.Saya agak terlambat ke pesta, tetapi dengan Django 1.6 ada
first()
metode pada querysets.https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.first
Contoh:
sumber
Saya tidak dapat berbicara dengan pengalaman Django tetapi opsi # 1 dengan jelas memberitahu sistem bahwa Anda meminta 1 objek, sedangkan opsi kedua tidak. Ini berarti bahwa opsi # 1 dapat lebih mudah memanfaatkan cache atau indeks basis data, terutama di mana atribut yang Anda filter tidak dijamin unik.
Juga (lagi, berspekulasi) opsi kedua mungkin harus membuat semacam koleksi hasil atau objek iterator karena panggilan filter () biasanya bisa mengembalikan banyak baris. Anda akan melewati ini dengan get ().
Akhirnya, opsi pertama lebih pendek dan menghilangkan variabel sementara ekstra - hanya perbedaan kecil tetapi setiap sedikit membantu.
sumber
Mengapa semua itu berhasil? Ganti 4 baris dengan 1 pintasan bawaan. (Ini mencoba / kecuali sendiri.)
sumber
Model.objects.get_or_create()
bagiBeberapa info lebih lanjut tentang pengecualian. Jika mereka tidak dibesarkan, mereka hampir tidak memerlukan biaya apa pun. Jadi, jika Anda tahu Anda mungkin akan mendapatkan hasil, gunakan pengecualian, karena menggunakan ekspresi bersyarat Anda membayar biaya memeriksa setiap waktu, tidak peduli apa. Di sisi lain, harganya sedikit lebih mahal daripada ekspresi kondisional ketika mereka dinaikkan, jadi jika Anda berharap tidak memiliki hasil dengan frekuensi tertentu (katakanlah, 30% dari waktu, jika memori berfungsi), pemeriksaan bersyarat ternyata menjadi sedikit lebih murah.
Tetapi ini adalah ORANG Django, dan mungkin bolak-balik ke basis data, atau bahkan hasil yang di-cache, cenderung mendominasi karakteristik kinerja, jadi nikmatilah keterbacaan, dalam hal ini, karena Anda mengharapkan tepat satu hasil, gunakan
get()
.sumber
Saya telah bermain dengan masalah ini sedikit dan menemukan bahwa opsi 2 mengeksekusi dua query SQL, yang untuk tugas sederhana seperti itu berlebihan. Lihat anotasi saya:
Versi yang setara yang mengeksekusi satu query adalah:
Dengan beralih ke pendekatan ini, saya dapat secara substansial mengurangi jumlah kueri yang dijalankan aplikasi saya.
sumber
Pertanyaan yang menarik, tetapi bagi saya opsi # 2 berbau optimasi prematur. Saya tidak yakin mana yang lebih performan, tetapi opsi # 1 tentu terlihat dan terasa lebih pythonic bagi saya.
sumber
Saya menyarankan desain yang berbeda.
Jika Anda ingin melakukan fungsi pada hasil yang mungkin, Anda bisa berasal dari QuerySet, seperti ini: http://djangosnippets.org/snippets/734/
Hasilnya cukup mengagumkan, Anda bisa misalnya:
Di sini, filter mengembalikan queryset kosong atau queryset dengan satu item. Fungsi queryset khusus Anda juga dapat diputus-putus dan dapat digunakan kembali. Jika Anda ingin melakukannya untuk semua entri Anda:
MyModel.objects.all().yourFunction()
.Mereka juga ideal untuk digunakan sebagai tindakan di antarmuka admin:
sumber
Opsi 1 lebih elegan, tetapi pastikan untuk menggunakan try..exception.
Dari pengalaman saya sendiri, saya dapat memberi tahu Anda bahwa kadang-kadang Anda yakin tidak mungkin ada lebih dari satu objek yang cocok dalam database, namun akan ada dua ... (kecuali tentu saja ketika mendapatkan objek dengan kunci utamanya).
sumber
Maaf menambahkan satu lagi mengambil masalah ini, tapi saya menggunakan pagjinator Django, dan di aplikasi admin data saya, pengguna diizinkan untuk memilih apa yang ingin ditanyakan. Kadang-kadang itu adalah id dokumen, tetapi sebaliknya itu adalah permintaan umum yang mengembalikan lebih dari satu objek, yaitu Queryset.
Jika pengguna menanyakan id, saya bisa menjalankan:
yang melempar kesalahan dalam paginator Django, karena itu adalah Record dan bukan Queryset of Records.
Saya perlu menjalankan:
Yang mengembalikan Queryset dengan satu item di dalamnya. Kemudian paginator berfungsi dengan baik.
sumber
.Dapatkan()
.Saring()
Catatan
sumber