Dobre sposoby sortowania zestawu zapytań? - Django

112

co próbuję zrobić to:

  • zdobądź 30 autorów z najwyższym wynikiem ( Author.objects.order_by('-score')[:30])

  • zamów autorów wg last_name


Jakieś sugestie?

RadiantHex
źródło
4
@RH: co powiesz na sprawdzenie odpowiedzi AlexMartellego jako poprawnego rozwiązania? (nie żeby potrzebował więcej powtórzeń, chyba że idzie za tym facetem ze Skeeta ...)
PaulMcG

Odpowiedzi:

191

Co powiesz na

import operator

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

W Django 1.4 i nowszych można składać zamówienia, udostępniając wiele pól.
Źródła: https://docs.djangoproject.com/en/dev/ref/models/querysets/#order-by

order_by (* pola)

Domyślnie wyniki zwracane przez a QuerySetsą uporządkowane według krotki porządkowania podanej przez orderingopcję w Meta modelu. Możesz to zmienić na podstawie QuerySet przy użyciu order_bymetody.

Przykład:

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

Powyższy wynik zostanie uporządkowany scoremalejąco, a następnie last_namerosnąco. Znak minus przed "-score"oznacza malejącą kolejność. Domniemany jest porządek rosnący.

Alex Martelli
źródło
1
@Alex: grazie alex! To było świetne, idę poczytać o module operatorskim!
RadiantHex,
3
Czy to jest bardziej wydajne niż Author.objects.order_by („- score”, „last_name”) [: 30]?
Brian Luft
4
@Brian - jeśli przez „bardziej wydajne” masz na myśli „bardziej poprawne”, to tak, jego. :) Twoje rozwiązanie pozwala na sortowanie autorów według punktacji i alfabetowanie tylko tych autorów z tym samym wynikiem (tak działają klawisze drugorzędne). Alex pokazuje, jak wziąć wyniki, a następnie zastosować do nich zupełnie inną kolejność sortowania (używając key = operator.attrgetter do zdefiniowania wyrażenia kluczowego dla obiektu), o co prosił OP.
PaulMcG,
@Paul: nie o to jednak pytałem, odpowiedź Alexa jest prawidłowa!
RadiantHex
4
Dlaczego nie wykorzystać funkcji klucza sortowania lambda x: x.last_name? Jest krótszy, bardziej szczegółowy i nie wymaga importu.
Krzysztof Szularz 13.12.13
12

Chciałem tylko zilustrować, że wbudowane rozwiązania (tylko SQL) nie zawsze są najlepsze. Na początku pomyślałem, że QuerySet.objects.order_byskoro metoda Django przyjmuje wiele argumentów, możesz łatwo połączyć je w łańcuch:

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

Ale to nie działa tak, jak można by się tego spodziewać. Przykładowo, pierwsza to lista prezydentów posortowana według punktacji (wybranie 5 najlepszych dla łatwiejszego czytania):

>>> 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)

Korzystając z rozwiązania Alexa Martelli, które dokładnie przedstawia 5 najlepszych osób posortowanych według 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)

A teraz połączone order_bypołączenie:

>>> 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)

Jak widać, jest to ten sam wynik, co pierwszy, co oznacza, że ​​nie działa zgodnie z oczekiwaniami.

jatanizm
źródło
13
Twój wynik nr 3 to sortowanie malejąco według wyniku, a następnie według last_name IFF wszystkie obiekty mają ten sam wynik. Problem polega na tym, że żaden z obiektów w twoim zestawie wyników nie ma tego samego wyniku, więc tylko „-score” wpływa na kolejność sortowania. Spróbuj ustawić wynik dla 3 autorów na 487 i ponownie uruchom numer 3.
istruble
Tak, rozumiem to. Naprawdę chciałem tylko zilustrować, że wbudowane rozwiązania (tylko SQL) nie zawsze są najlepsze.
jatanizm
3
Zrobił dokładnie to, czego się spodziewałem: porządkowanie leksykograficzne (co jest trochę trywialne, jeśli pierwszy klucz sortowania jest inny).
Jonas Kölker
5

Oto sposób, który pozwala na remisy dla końcowego wyniku.

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')

W ten sposób możesz znaleźć więcej niż 30 autorów w top_authors, a min(30,author_count)jeśli masz mniej niż 30 autorów.

istruble
źródło