I have a dict made up of (id, rank) pairs. I'd like to perform a Django query on the ids such that the resultant queryset is ordered by rank (descending).
Getting the queryset is easy:
rankings = {...}
result = MyModel.objects.filter(id__in=rankings.keys())
It seems like the answer should involve some sort of annotation that I can use as part of the order_by but I can't figure out how to get there.
EDIT: I neglected to mention that I need the result to be a QuerySet as this is part of a tastypie API pipeline.
My solution for Django > 1.10 and PostgreSQL > 9.5
from django.db.models import Func, Value, IntegerField, CharField
from django.contrib.postgres.fields import ArrayField
class ArrayPosition(Func):
function = 'array_position'
def __init__(self, items, *expressions, **extra):
if isinstance(items[0], int):
base_field = IntegerField()
else:
base_field = CharField(max_length=max(len(i) for i in items))
first_arg = Value(list(items), output_field=ArrayField(base_field))
expressions = (first_arg, ) + expressions
super().__init__(*expressions, **extra)
pk_list = [234,12,23]
queryset = SomeModel.objects.filter(pk__in=pk_list, ...)\
.annotate(ordering=ArrayPosition(pk_list, F('pk'), output_field=IntegerField()))\
.order_by('ordering')
Something like this?
rankings = { 1 : 2, 2: 1, ... } # i.e. { 'id' : 'ranking', ... }
objects = list(MyModel.objects.filter(id__in=rankings.keys()))
objects.sort(key=lambda obj: rankings[obj.id])
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