Paul
Paul

Reputation: 6747

Reorder Django QuerySet by dynamically added field

A have piece of code, which fetches some QuerySet from DB and then appends new calculated field to every object in the Query Set. It's not an option to add this field via annotation (because it's legacy and because this calculation based on another already pre-fetched data).

Like this:

from django.db import models
class Human(models.Model):
    name = models.CharField()
    surname = models.CharField()

def calculate_new_field(s):
   return len(s.name)*42

people = Human.objects.filter(id__in=[1,2,3,4,5])

for s in people:
   s.new_column = calculate_new_field(s)
# people.somehow_reorder(new_order_by=new_column)

So now all people in QuerySet have a new column. And I want order these objects by new_column field. order_by() will not work obviously, since it is a database option. I understand thatI can pass them as a sorted list, but there is a lot of templates and other logic, which expect from this object QuerySet-like inteface with it's methods and so on.

So question is: is there some not very bad and dirty way to reorder existing QuerySet by dinamically added field or create new QuerySet-like object with this data? I believe I'm not the only one who faced this problem and it's already solved with django. But I can't find anything (except for adding third-party libs, and this is not an option too).

Upvotes: 1

Views: 1892

Answers (2)

tato
tato

Reputation: 5559

Conceptually, the QuerySet is not a list of results, but the "instructions to get those results". It's lazily evaluated and also cached. The internal attribute of the QuerySet that keeps the cached results is qs._result_cache

So, the for s in people sentence is forcing the evaluation of the query and caching the results.

You could, after that, sort the results by doing:

people._result_cache.sort(key=attrgetter('new_column'))

But, after evaluating a QuerySet, it makes little sense (in my opinion) to keep the QuerySet interface, as many of the operations will cause a reevaluation of the query. From this point on you should be dealing with a list of Models

Upvotes: 1

user8060120
user8060120

Reputation:

Can you try it functions.Length:

from django.db.models.functions import Length

qs = Human.objects.filter(id__in=[1,2,3,4,5])
qs.annotate(reorder=Length('name') * 42).order_by('reorder')

Upvotes: 0

Related Questions