Django QuerySet按ID自定义排序

Mat*_*tin 15 python django django-queryset

给定一个id/pks列表,我想生成一个QuerySet由列表中的索引排序的对象.

通常我会先说:

pk_list = [5, 9, 2, 14]
queryset = MyModel.objects.filter(pk__in=pk_list)
Run Code Online (Sandbox Code Playgroud)

当然,这将返回的对象,但在模型元排序属性的顺序,我希望得到的顺序记录pk以秒pk_list.

最终结果必须是一个 QuerySet对象(不是列表),因为我希望将有序文件传递QuerySet给Django的ModelMultipleChoiceField表单字段.

Dan*_*man 10

没有内置的方法来做到这一点.

如果您使用的是MySQL,则可以使用该数据库的FIELD()函数在模型上设置自定义排序顺序,并按此排序.这可行:

pk_list = [5, 9, 2, 14]
ordering = 'FIELD(`id`, %s)' % ','.join(str(id) for id in pk_list)
queryset = MyModel.objects.filter(pk__in=[pk_list]).extra(
               select={'ordering': ordering}, order_by=('ordering',))
Run Code Online (Sandbox Code Playgroud)


ARK*_*han 5

Django 2.2中,我可以使用以下方法根据订单列表对 QuerySet 进行排序:

pk_list = [4, 23, 10, 9]
preserved = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(pk_list)])
new_qs = qs.filter(pk__in=pk_list).order_by(preserved)
Run Code Online (Sandbox Code Playgroud)


Man*_*dan 1

我不知道如何使用过滤条件来做到这一点。如果您的数据库将按照子句的顺序返回行,IN那么您可以extra将 Django 的方法与ROWNUM数据库提供的方法结合起来以实现此目的。

例如:

queryset = MyModel.objects.filter(pk__in=[pk_list]).extra(
       select: {'rownum': 'row_num()'}).order_by('rownum')
Run Code Online (Sandbox Code Playgroud)

其中row_num()假设是一个数据库函数,返回当前行的行号。Postgresql 8.4+ 支持,row_num()但我不知道如何使用与子句中的值相同的顺序对返回的行进行排序IN

我认为更好的方法是ModelMultipleChoiceField在渲染时子类化并添加自定义排序逻辑。