views:

179

answers:

3

Hi folks,

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?

+10  A: 

What about

import operator

auths = Author.objects.order_by('-score')[:30]
ordered = sorted(auths, key=operator.attrgetter('last_name'))
Alex Martelli
@Alex: Aleeeexxxx!!!
RadiantHex
@Alex: grazie alex! That was great, I'm off to read about the operator module!
RadiantHex
Martelli rockin' it as per usual...
Adam Nelson
Is this more efficient than Author.objects.order_by('-score', 'last_name')[:30]?
Brian Luft
@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 McGuire
@Paul: that's not what I asked though, Alex's answer is the correct one!
RadiantHex
@RH: Sorry for my obtuseness of expression - I was trying to say the same thing, that Alex's posted solution, as usual, is the correct one. @jathanism nicely walks through why using multiple args to order_by gives a result that does not match your original request.
Paul McGuire
+1  A: 

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.

istruble
+1  A: 

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.

jathanism
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.
istruble
Yeah I understand that. I really just wanted to illustrate that the built-in solutions (SQL-only) are not always the best ones.
jathanism
+1 Good example of counter-intuitive ORDER BY clause
Paul McGuire