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 :)
If you want to add sorting on a calculated field, you have to tell Django what to pass to order_by . You can do this by setting the admin_order_field attribute on the calculated field method.
F() can be used to create dynamic fields on your models by combining different fields with arithmetic: company = Company. objects. annotate( chairs_needed=F('num_employees') - F('num_chairs')) If the fields that you're combining are of different types you'll need to tell Django what kind of field will be returned.
When specifying the field to be aggregated in an aggregate function, Django will allow you to use the same double underscore notation that is used when referring to related fields in filters. Django will then handle any table joins that are required to retrieve and aggregate the related value.
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)
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:
order_by
See also this SO Q&A: Order a QuerySet by aggregate field value
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With