How do I sort a Django QuerySet by an external value?

百般思念 提交于 2021-02-07 14:25:30

问题


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.


回答1:


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')



回答2:


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])



回答3:


The only way I could figure out to solve this is to create a new ranking model related to primary model. On each query I insert the ranking items into that model and then am able to execute the order_by via the relation. (Using annotate to add the rank to the school records.)

class UserSchoolRanking(models.Model):
    user = models.ForeignKey(User)
    school = models.ForeignKey(School)
    rank = models.IntegerField()

bulk_user_school_rank = [UserSchoolRank(user=user, school_id=k, rank=v)
                                         for k, v in rankings.iteritems()]
UserSchoolRank.objects.bulk_create(bulk_user_school_rank)

schools = School.objects.filter(userschoolrank__user=user)\
                .annotate(rank=Min('userschoolrank__rank'))\
                .order_by('-userschoolrank__rank')


来源:https://stackoverflow.com/questions/13040052/how-do-i-sort-a-django-queryset-by-an-external-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!