De bons moyens de trier un ensemble de requêtes? - Django

112

ce que j'essaye de faire est ceci:

  • obtenir les 30 auteurs avec le score le plus élevé ( Author.objects.order_by('-score')[:30])

  • ordonner les auteurs par last_name


Aucune suggestion?

RadiantHex
la source
4
@RH: alors pourquoi ne pas vérifier la réponse d'AlexMartelli comme étant la bonne solution? (pas qu'il ait besoin de plus de représentants, à moins qu'il ne poursuive ce gars de Skeet ...)
PaulMcG

Réponses:

191

Qu'en est-il de

import operator

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

Dans Django 1.4 et plus récent, vous pouvez commander en fournissant plusieurs champs.
Référence: https://docs.djangoproject.com/en/dev/ref/models/querysets/#order-by

order_by (* champs)

Par défaut, les résultats renvoyés par a QuerySetsont triés par le tuple de classement donné par l' orderingoption dans la méta du modèle. Vous pouvez remplacer cela sur une base par QuerySet à l'aide de la order_byméthode.

Exemple:

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

Le résultat ci-dessus sera ordonné par ordre scoredécroissant, puis par ordre last_namecroissant. Le signe négatif devant "-score"indique l'ordre décroissant. L'ordre croissant est implicite.

Alex Martelli
la source
1
@Alex: grazie alex! C'était génial, je suis parti pour lire sur le module opérateur!
RadiantHex
3
Est-ce plus efficace que Author.objects.order_by ('- score', 'last_name') [: 30]?
Brian Luft
4
@Brian - si par "plus efficace" vous voulez dire "plus correct" alors oui, c'est. :) Votre solution maintient les auteurs assez bien triés par score, et ne classe par ordre alphabétique que les auteurs avec le même score (c'est ainsi que fonctionnent les touches secondaires). Alex montre comment prendre les résultats, puis leur appliquer un ordre de tri complètement différent (en utilisant key = operator.attrgetter pour définir une expression de clé par objet), ce que l'OP a demandé.
PaulMcG
@Paul: ce n'est pas ce que j'ai demandé, la réponse d'Alex est la bonne!
RadiantHex
4
Pourquoi ne pas faire fonctionner la touche de tri lambda x: x.last_name? C'est plus court, plus détaillé et ne nécessite aucune importation.
Krzysztof Szularz
12

Je voulais juste illustrer que les solutions intégrées (SQL uniquement) ne sont pas toujours les meilleures. Au début, je pensais que la QuerySet.objects.order_byméthode de Django acceptant plusieurs arguments, vous pouviez facilement les chaîner:

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

Mais cela ne fonctionne pas comme prévu. Par exemple, la première est une liste de présidents triés par score (en sélectionnant les 5 premiers pour une lecture plus 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)

En utilisant la solution d'Alex Martelli qui fournit avec précision le top 5 des personnes triées par 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)

Et maintenant l' order_byappel combiné :

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

Comme vous pouvez le voir, c'est le même résultat que le premier, ce qui signifie que cela ne fonctionne pas comme prévu.

jathanisme
la source
13
Votre résultat n ° 3 est un tri décroissant par score, puis par last_name IFF, tous les objets ont le même score. Le problème est qu'aucun des objets de votre jeu de résultats n'a le même score, donc seul le «-score» affecte l'ordre de tri. Essayez de définir le score de 3 auteurs sur 487 et exécutez à nouveau le numéro 3.
istruble
Ouais je comprends ça. Je voulais vraiment juste illustrer que les solutions intégrées (SQL uniquement) ne sont pas toujours les meilleures.
jathanism
3
Il a fait exactement ce à quoi je m'attendais: l'ordre lexicographique (ce qui est un peu trivial si la première clé de tri est toute distincte).
Jonas Kölker
5

Voici un moyen qui permet des égalités pour le score de coupure.

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

Vous pouvez obtenir plus de 30 auteurs dans top_authors de cette façon et si min(30,author_count)vous avez moins de 30 auteurs.

istruble
la source