Buoni modi per ordinare un set di query? - Django


112

quello che sto cercando di fare è questo:

  • ottieni i 30 autori con il punteggio più alto ( Author.objects.order_by('-score')[:30])

  • ordina gli autori per last_name


Eventuali suggerimenti?


4
@ RH: quindi che ne dici di controllare la risposta di AlexMartelli come la soluzione corretta? (non che abbia bisogno di altre rappresentazioni, a meno che non stia cercando quel ragazzo di Skeet ...)
PaulMcG

Risposte:


191

Che dire

import operator

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

In Django 1.4 e versioni successive è possibile ordinare fornendo più campi.
Riferimento: https://docs.djangoproject.com/en/dev/ref/models/querysets/#order-by

order_by (* campi)

Per impostazione predefinita, i risultati restituiti da a QuerySetsono ordinati in base alla tupla di ordinamento fornita orderingdall'opzione nel Meta del modello. È possibile sovrascriverlo in base a QuerySet utilizzando il order_bymetodo.

Esempio:

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

Il risultato sopra sarà ordinato per ordine scorediscendente, quindi last_namecrescente. Il segno negativo davanti a "-score"indica l'ordine decrescente. L'ordine crescente è implicito.


1
@Alex: grazie alex! È stato fantastico, vado a leggere del modulo operatore!
RadiantHex

3
È più efficiente di Author.objects.order_by ('- score', 'last_name') [: 30]?
Brian Luft

4
@ Brian - se con "più efficiente" intendi "più corretto", allora sì, è. :) La tua soluzione mantiene gli autori praticamente ordinati in base al punteggio e alfabetizza solo quegli autori con lo stesso punteggio (che è il modo in cui funzionano le chiavi secondarie). Alex mostra come prendere i risultati e quindi applicare loro un ordinamento completamente diverso (usando key = operator.attrgetter per definire un'espressione chiave per oggetto), che è ciò che l'OP ha chiesto.
PaulMcG

@Paul: non è quello che ho chiesto però, la risposta di Alex è quella corretta!
RadiantHex

4
Perché non rendere la funzione chiave di ordinamento lambda x: x.last_name? È più breve, più dettagliato e non necessita di alcuna importazione.
Krzysztof Szularz

12

Volevo solo illustrare che le soluzioni integrate (solo SQL) non sono sempre le migliori. All'inizio ho pensato che, poiché il QuerySet.objects.order_bymetodo di Django accetta più argomenti, potresti facilmente concatenarli:

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

Ma non funziona come ti aspetteresti. Caso in questione, il primo è un elenco di presidenti ordinati per punteggio (selezionando i primi 5 per una lettura più facile):

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

Utilizzando la soluzione di Alex Martelli che fornisce accuratamente le prime 5 persone ordinate per 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)

E ora la order_bychiamata combinata :

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

Come puoi vedere è lo stesso risultato del primo, il che significa che non funziona come ti aspetteresti.


13
Il tuo risultato n. 3 è l'ordinamento decrescente per punteggio e poi per last_name IFF tutti gli oggetti hanno lo stesso punteggio. Il problema è che nessuno degli oggetti nel tuo set di risultati ha lo stesso punteggio, quindi solo il "punteggio" influenza l'ordinamento. Prova a impostare il punteggio di 3 autori su 487 e ripeti il ​​numero 3.
istruble

Sì, lo capisco. Volevo solo illustrare che le soluzioni integrate (solo SQL) non sono sempre le migliori.
jathanism

3
Ha fatto esattamente quello che mi aspettavo: ordinamento lessicografico (che è un po 'banale se la prima chiave di ordinamento è tutta distinta).
Jonas Kölker

5

Ecco un modo che consente i pareggi per il punteggio limite.

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

Puoi avere più di 30 autori in top_authors in questo modo ed min(30,author_count)è possibile che tu abbia meno di 30 autori.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.