Cara yang baik untuk mengurutkan queryset? - Django

112

apa yang saya coba lakukan adalah ini:

  • dapatkan 30 Penulis dengan skor tertinggi ( Author.objects.order_by('-score')[:30])

  • pesan penulis menurut last_name


Ada saran?

RadiantHex
sumber
4
@RH: jadi bagaimana kalau memeriksa jawaban AlexMartelli sebagai solusi yang benar? (bukan berarti dia membutuhkan lebih banyak perwakilan, kecuali dia mengejar pria Skeet itu ...)
PaulMcG

Jawaban:

191

Bagaimana dengan

import operator

auths = Author.objects.order_by('-score')[:30]
ordered = sorted(auths, key=operator.attrgetter('last_name'))

Dalam Django 1.4 dan yang lebih baru Anda dapat memesan dengan menyediakan banyak bidang.
Referensi: https://docs.djangoproject.com/en/dev/ref/models/querysets/#order-by

order_by (* bidang)

Secara default, hasil yang dikembalikan oleh a QuerySetdiurutkan oleh tupel pemesanan yang diberikan oleh orderingopsi dalam model Meta. Anda dapat menimpa ini pada basis per-QuerySet dengan menggunakan order_bymetode ini.

Contoh:

ordered_authors = Author.objects.order_by('-score', 'last_name')[:30]

Hasil di atas akan diurutkan dengan scoreturun, lalu last_namenaik. Tanda negatif di depan "-score"menunjukkan urutan menurun. Urutan menaik tersirat.

Alex Martelli
sumber
1
@Alex: grazie alex! Itu bagus, saya akan membaca tentang modul operator!
RadiantHex
3
Apakah ini lebih efisien daripada Author.objects.order_by ('- score', 'last_name') [: 30]?
Brian Luft
4
@ Brian - jika dengan "lebih efisien" yang Anda maksud "lebih benar" maka ya, itu. :) Solusi Anda membuat penulis cukup banyak diurutkan berdasarkan skor, dan hanya menyusun menurut abjad penulis tersebut dengan skor yang sama (begitulah cara kerja tombol sekunder). Alex menunjukkan bagaimana mengambil hasil, dan kemudian menerapkan urutan yang sama sekali berbeda untuk mereka (menggunakan key = operator.attrgetter untuk mendefinisikan ekspresi kunci per objek), yang diminta OP.
PaulMcG
@Paul: Tapi bukan itu yang saya tanyakan, jawaban Alex yang benar!
RadiantHex
4
Mengapa tidak membuat fungsi tombol sortir lambda x: x.last_name? Lebih pendek, lebih bertele-tele dan tidak perlu diimpor.
Krzysztof Szularz
12

Saya hanya ingin mengilustrasikan bahwa solusi bawaan (hanya SQL) tidak selalu yang terbaik. Pada awalnya saya berpikir karena QuerySet.objects.order_bymetode Django menerima banyak argumen, Anda dapat dengan mudah merangkainya:

ordered_authors = Author.objects.order_by('-score', 'last_name')[:30]

Tapi, itu tidak berfungsi seperti yang Anda harapkan. Contoh kasus, pertama adalah daftar presiden yang diurutkan berdasarkan skor (memilih 5 teratas agar lebih mudah dibaca):

>>> auths = Author.objects.order_by('-score')[:5]
>>> for x in auths: print x
... 
James Monroe (487)
Ulysses Simpson (474)
Harry Truman (471)
Benjamin Harrison (467)
Gerald Rudolph (464)

Menggunakan solusi Alex Martelli yang secara akurat memberikan 5 orang teratas yang diurutkan berdasarkan last_name:

>>> for x in sorted(auths, key=operator.attrgetter('last_name')): print x
... 
Benjamin Harrison (467)
James Monroe (487)
Gerald Rudolph (464)
Ulysses Simpson (474)
Harry Truman (471)

Dan sekarang order_bypanggilan gabungan :

>>> myauths = Author.objects.order_by('-score', 'last_name')[:5]
>>> for x in myauths: print x
... 
James Monroe (487)
Ulysses Simpson (474)
Harry Truman (471)
Benjamin Harrison (467)
Gerald Rudolph (464)

Seperti yang Anda lihat, ini adalah hasil yang sama dengan yang pertama, artinya tidak berfungsi seperti yang Anda harapkan.

jatanisme
sumber
13
Hasil # 3 Anda adalah mengurutkan menurun berdasarkan skor dan kemudian menurut nama_akhir IFF objek apa pun memiliki skor yang sama. Masalahnya adalah tidak ada objek dalam set hasil Anda yang memiliki skor yang sama sehingga hanya '-score' yang memengaruhi urutan pengurutan. Coba setel skor 3 penulis menjadi 487 dan jalankan # 3 lagi.
tersedia
Ya saya mengerti itu. Saya benar-benar hanya ingin mengilustrasikan bahwa solusi bawaan (hanya SQL) tidak selalu yang terbaik.
Jatanisme
3
Itu persis seperti yang saya harapkan: pengurutan leksikografis (yang agak sepele jika kunci penyortiran pertama semuanya berbeda).
Jonas Kölker
5

Inilah cara yang memungkinkan ikatan untuk skor cut-off.

author_count = Author.objects.count()
cut_off_score = Author.objects.order_by('-score').values_list('score')[min(30, author_count)]
top_authors = Author.objects.filter(score__gte=cut_off_score).order_by('last_name')

Anda bisa mendapatkan lebih dari 30 penulis di top_authors dengan cara ini dan min(30,author_count)apakah ada jika Anda memiliki kurang dari 30 penulis.

tidak bisa rusak
sumber