7

Hey I want to sort objects based on a computed value in django... how do I do it?

Here is an example User profile model based on stack overflow that explains my predicament:

class Profile(models.Model):
    user = models.ForeignKey(User)

    def get_reputation():
        ... 
        return reputation
    reputation = property(get_reputation)

So, say I want to sort users by reputation. How do I do that? I know you can't just do this:

Profile.objects.order_by("-reputation")

Thanks for your help everyone :)

Jiaaro
  • 74,485
  • 42
  • 169
  • 190

3 Answers3

16

Since your calculation code exists only within Python, you have to perform the sorting in Python as well:

sorted (Profile.objects.all (), key = lambda p: p.reputation)
John Millikin
  • 197,344
  • 39
  • 212
  • 226
  • hey, this is the answer to the question I asked, but I accepted the other one because it solved the problem I have... specifically S.Lott's comment. I upmodded your answer though :) – Jiaaro May 31 '09 at 02:52
  • is there a way to make this return a queryset instead of a regular python list (or convert a list to a queryset)? – Jiaaro Jun 29 '09 at 12:37
  • That seems like a bit heavy to ask in a comment, so I moved it to it's own question: http://stackoverflow.com/questions/1058135/django-convert-a-list-back-to-a-queryset – Jiaaro Jun 29 '09 at 12:59
7

As of Django-1.8, it is possible to sort a QuerySet using query expressions as long as the computed value can be rendered in SQL. You can also use annotations to the model as the order_by term.

from django.db.models import F
Profile.objects.annotate(reputation=(F('<field>') + ...)).order_by("-reputation")

Basic operations like +, -, *, / and ** can be used with F(). In addition there are several other functions such as Count, Avg, Max, ...

Links:

See also this SO Q&A: Order a QuerySet by aggregate field value

Community
  • 1
  • 1
Mark Mikofski
  • 19,398
  • 2
  • 57
  • 90
4

If you need to do the sorting in the database (because you have lots of records, and need to e.g. paginate them), the only real option is to turn reputation into a denormalized field (e.g. updated in an overridden save() method on the model).

Carl Meyer
  • 122,012
  • 20
  • 106
  • 116
  • I decided to use the post-save signal rather than overriding save()... are there any potential gotchas I should know about with that approach? – Jiaaro May 31 '09 at 17:13
  • If you want to use signals, I'd recommend pre_save rather than post_save. To keep a denormalized field up to date with post_save means you have to do two UPDATE queries where one would suffice. – Carl Meyer Jun 01 '09 at 03:07
  • @Carl Meyer Thanks Carl... I'm actually updating a different model than the one that's being saved, but thanks... I think the rule of thumb is use post-save to update a different model from the one you're saving, and pre-save if it's the same model, no? – Jiaaro Jun 24 '09 at 15:02
  • @Jim Robert Sure, in that case you want post_save to ensure you only do the update if the model successfully saved. – Carl Meyer Jun 25 '09 at 14:36