Boas maneiras de classificar um queryset? - Django

111

o que estou tentando fazer é o seguinte:

  • obtenha os 30 autores com maior pontuação ( Author.objects.order_by('-score')[:30])

  • ordenar os autores por last_name


Alguma sugestão?

RadiantHex
fonte
4
@RH: então que tal verificar a resposta de AlexMartelli como a solução correta? (não que ele precise de mais representação, a menos que vá atrás daquele cara Skeet ...)
PaulMcG

Respostas:

190

A respeito

import operator

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

No Django 1.4 e mais recente, você pode fazer o pedido fornecendo vários campos.
Referência: https://docs.djangoproject.com/en/dev/ref/models/querysets/#order-by

order_by (* campos)

Por padrão, os resultados retornados por a QuerySetsão ordenados pela tupla de ordenação fornecida pela orderingopção no Meta do modelo. Você pode substituir isso por QuerySet usando o order_bymétodo.

Exemplo:

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

O resultado acima será ordenado em ordem scoredecrescente e depois em ordem last_namecrescente. O sinal negativo na frente de "-score"indica ordem decrescente. A ordem crescente está implícita.

Alex Martelli
fonte
1
@Alex: grazie alex! Foi ótimo, vou ler sobre o módulo do operador!
RadiantHex,
3
Isso é mais eficiente do que Author.objects.order_by ('- score', 'last_name') [: 30]?
Brian Luft
4
@Brian - se por "mais eficiente" você quer dizer "mais correto", então sim, é. :) Sua solução mantém os autores praticamente classificados por pontuação, e só coloca em ordem alfabética aqueles autores com a mesma pontuação (que é como funcionam as chaves secundárias). Alex mostra como obter os resultados e, em seguida, aplicar uma ordem de classificação completamente diferente a eles (usando key = operator.attrgetter para definir uma expressão de chave por objeto), que é o que o OP pediu.
PaulMcG de
@Paul: não foi isso que eu perguntei, a resposta de Alex é a correta!
RadiantHex
4
Por que não fazer a tecla de classificação funcionar lambda x: x.last_name? É mais curto, mais detalhado e não precisa de nenhuma importação.
Krzysztof Szularz
12

Eu só queria ilustrar que as soluções integradas (somente SQL) nem sempre são as melhores. A princípio pensei que, como o QuerySet.objects.order_bymétodo do Django aceita vários argumentos, você poderia facilmente encadea-los:

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

Mas não funciona como você esperaria. Caso em questão, primeiro está uma lista de presidentes classificados por pontuação (selecionando os 5 primeiros para facilitar a leitura):

>>> 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 a solução de Alex Martelli, que fornece com precisão as 5 principais pessoas classificadas 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)

E agora a order_bychamada 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 você pode ver, é o mesmo resultado do primeiro, o que significa que não funciona como você esperava.

jatanismo
fonte
13
Seu resultado # 3 é uma classificação decrescente por pontuação e, em seguida, por last_name IFF, todos os objetos têm a mesma pontuação. O problema é que nenhum dos objetos em seu conjunto de resultados tem a mesma pontuação, portanto, apenas o '-score' está afetando a ordem de classificação. Tente definir a pontuação de 3 autores para 487 e execute o # 3 novamente.
istruble de
Sim, eu entendo isso. Eu realmente queria apenas ilustrar que as soluções integradas (somente SQL) nem sempre são as melhores.
jathanismo de
3
Fez exatamente o que eu esperava: ordenação lexicográfica (que é um tanto trivial se a primeira chave de ordenação for totalmente distinta).
Jonas Kölker,
5

Esta é uma maneira que permite empates para a pontuação 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')

Você pode obter mais de 30 autores em top_authors dessa forma e min(30,author_count)há no caso de você ter menos de 30 autores.

istrúvel
fonte