Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort queryset by values in list

Tags:

python

django

Is it possible to sort a django queryset by the list of elements provided in the query? For example, if I do

m.objects.filter(id__in=[3,1,8])

I wan't the order of the queryset to be the element of id 3, the element of id 1 and the element of id 8.

Thanks

like image 669
Vitor Avatar asked May 11 '11 15:05

Vitor


2 Answers

Since there exists Case and When in Django >= 1.11 you can do it in a more orm-like way keeping all the benefits of your queryset:

from django.db import models

order = ['b', 'a', 'z', 'x', 'c']
_whens = []

for sort_index, value in enumerate(order):
    _whens.append(
        models.When(my_field=value, then=sort_index)
    )

qs = MyModel.objects.annotate(
        _sort_index=models.Case(
            *_whens, 
            output_field=models.IntegerField()
        )
     )

qs.order_by('_sort_index')

This will generate something like this:

from django.db import models

order = ['b', 'a', 'z', 'x', 'c']
qs = MyModel.objects.annotate(
    _sort_index=models.Case(
        models.When(my_field='b', then=0),
        models.When(my_field='a', then=1),
        models.When(my_field='z', then=2),
        models.When(my_field='x', then=3),
        models.When(my_field='c', then=4),
        output_field=models.IntegerField()
    )
).order_by('_sort_index')

I would suggest to use this only with a small amount of list-items because it blows up the database-query.

https://docs.djangoproject.com/ko/1.11/ref/models/conditional-expressions/#case

like image 135
chsymann Avatar answered Oct 11 '22 14:10

chsymann


No. There is no way to do what you want short of some contrived mechanism like:

qs = m.objects.filter(id__in=[3,1,8])
qs_sorted = list()
for id in [3,1,8]:
    qs_sorted.append(qs.get(id=id))

You'd need to throw some exception handling in there as well, just in case one of the specified ids wasn't actually returned.

The negatives are that this will effectively negate the lazy loading of queryset. In fact, it's very likely to generate multiple DB queries (haven't tested that though). Also, you end up with a normal python list instead of a queryset, so no more filtering is possible.

like image 26
Chris Pratt Avatar answered Oct 11 '22 14:10

Chris Pratt