ChatGPT解决这个技术问题 Extra ChatGPT

Good ways to sort a queryset? - Django

what I'm trying to do is this:

get the 30 Authors with highest score ( Author.objects.order_by('-score')[:30] )

order the authors by last_name

Any suggestions?

@RH: so how about checking AlexMartelli's answer as the correct solution? (not that he needs any more rep, unless he's going after that Skeet guy...)

M
Martin

What about

import operator

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

In Django 1.4 and newer you can order by providing multiple fields.
Reference: https://docs.djangoproject.com/en/dev/ref/models/querysets/#order-by

order_by(*fields)

By default, results returned by a QuerySet are ordered by the ordering tuple given by the ordering option in the model’s Meta. You can override this on a per-QuerySet basis by using the order_by method.

Example:

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

The result above will be ordered by score descending, then by last_name ascending. The negative sign in front of "-score" indicates descending order. Ascending order is implied.


@Alex: grazie alex! That was great, I'm off to read about the operator module!
Is this more efficient than Author.objects.order_by('-score', 'last_name')[:30]?
@Brian - if by "more efficient" you mean "more correct" then yes, its. :) Your solution keeps the authors pretty much sorted by score, and only alphabetizes those authors with the same score (which is how secondary keys work). Alex shows how to take the results, and then apply a completely different sort order to them (using key=operator.attrgetter to define a per-object key expression), which is what the OP asked for.
@Paul: that's not what I asked though, Alex's answer is the correct one!
Why not making the sorting key function lambda x: x.last_name? It's shorter, more verbose and doesn't need any import.
j
jathanism

I just wanted to illustrate that the built-in solutions (SQL-only) are not always the best ones. At first I thought that because Django's QuerySet.objects.order_by method accepts multiple arguments, you could easily chain them:

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

But, it does not work as you would expect. Case in point, first is a list of presidents sorted by score (selecting top 5 for easier reading):

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

Using Alex Martelli's solution which accurately provides the top 5 people sorted by 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)

And now the combined order_by call:

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

As you can see it is the same result as the first one, meaning it doesn't work as you would expect.


Your result #3 is sorting descending by score and then by last_name IFF any objects have the same score. The problem is none of the objects in your result set have the same score so only the '-score' is affecting the sort order. Try setting 3 authors score to 487 and run #3 again.
Yeah I understand that. I really just wanted to illustrate that the built-in solutions (SQL-only) are not always the best ones.
It did exactly what I expected: lexicographic ordering (which is kinda' trivial if the first sorting key is all distinct).
i
istruble

Here's a way that allows for ties for the cut-off score.

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

You may get more than 30 authors in top_authors this way and the min(30,author_count) is there incase you have fewer than 30 authors.