¿Buenas formas de ordenar un conjunto de consultas? - Django

111

lo que estoy tratando de hacer es esto:

  • consigue los 30 Autores con mayor puntuación ( Author.objects.order_by('-score')[:30])

  • ordenar los autores por last_name


¿Alguna sugerencia?

RadiantHex
fuente
4
@RH: ¿qué tal si compruebas la respuesta de AlexMartelli como la solución correcta? (no es que necesite más representante, a menos que vaya tras ese tipo de Skeet ...)
PaulMcG

Respuestas:

190

Qué pasa

import operator

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

En Django 1.4 y versiones posteriores, puede ordenar proporcionando varios campos.
Referencia: https://docs.djangoproject.com/en/dev/ref/models/querysets/#order-by

order_by (* campos)

De forma predeterminada, los resultados devueltos por a QuerySetestán ordenados por la tupla de orden dada por la orderingopción en el Meta del modelo. Puede anular esto por cada QuerySet mediante el order_bymétodo.

Ejemplo:

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

El resultado anterior se ordenará scoredescendiendo y luego last_nameascendiendo. El signo negativo delante de "-score"indica un orden descendente. El orden ascendente está implícito.

Alex Martelli
fuente
1
@Alex: ¡Grazie Alex! Eso fue genial, ¡me voy a leer sobre el módulo del operador!
RadiantHex
3
¿Es esto más eficiente que Author.objects.order_by ('- score', 'last_name') [: 30]?
Brian Luft
4
@Brian: si por "más eficiente" te refieres a "más correcto", entonces sí, es. :) Su solución mantiene a los autores prácticamente ordenados por puntuación, y solo alfabetiza a los autores con la misma puntuación (que es como funcionan las claves secundarias). Alex muestra cómo tomar los resultados y luego aplicarles un orden de clasificación completamente diferente (usando key = operator.attrgetter para definir una expresión de clave por objeto), que es lo que pidió el OP.
PaulMcG
@Paul: eso no es lo que pregunté, ¡la respuesta de Alex es la correcta!
RadiantHex
4
¿Por qué no hacer que funcione la tecla de clasificación lambda x: x.last_name? Es más corto, más detallado y no necesita ninguna importación.
Krzysztof Szularz
12

Solo quería ilustrar que las soluciones integradas (solo SQL) no siempre son las mejores. Al principio pensé que debido a que el QuerySet.objects.order_bymétodo de Django acepta múltiples argumentos, podría encadenarlos fácilmente:

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

Pero no funciona como cabría esperar. Por ejemplo, primero hay una lista de presidentes ordenados por puntaje (seleccionando los 5 primeros para una lectura más fácil):

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

Usando la solución de Alex Martelli que proporciona con precisión las 5 personas principales ordenadas por 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)

Y ahora la order_byllamada combinada :

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

Como puede ver, es el mismo resultado que el primero, lo que significa que no funciona como cabría esperar.

jatismo
fuente
13
Su resultado n. ° 3 es ordenar descendiendo por puntaje y luego por last_name IFF cualquier objeto tiene el mismo puntaje. El problema es que ninguno de los objetos en su conjunto de resultados tiene la misma puntuación, por lo que solo la '-puntuación' afecta el orden de clasificación. Intente establecer la puntuación de 3 autores en 487 y vuelva a ejecutar el número 3.
istruble
Sí, lo entiendo. Realmente solo quería ilustrar que las soluciones integradas (solo SQL) no siempre son las mejores.
jathanismo
3
Hizo exactamente lo que esperaba: orden lexicográfico (que es un poco 'trivial si la primera clave de clasificación es distinta).
Jonas Kölker
5

Aquí hay una forma que permite empates para el puntaje de corte.

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

Puede obtener más de 30 autores en top_authors de esta manera y min(30,author_count)existe el caso de que tenga menos de 30 autores.

difícil
fuente