Django Queryset - 按字母顺序排序,然后将特定条目移到顶部

时间:2017-12-08 14:18:10

标签: django django-forms django-orm

编辑(背景信息)

我创建了一个自定义UserModelChoiceField类,该类接受用户的QuerySet并使用其全名填充选择字段:

class UserModelChoiceField(forms.ModelChoiceField):
    def label_from_instance(self, obj):
        return "{} ({})".format(obj.get_full_name(), obj.username)

我现在正试图使用​​这个类:

calibrated_by_internal = UserModelChoiceField(
                                              queryset=total_qs,
                                              required=False,
                                              widget=forms.Select(attrs={"class": "form-control"})
                                              )

原始问题

我试图创建一个自定义排序的QuerySet,它允许我在按字母顺序排序的表单上放置User ModelChoice选择字段,但是特定权限组的3个成员放在顶部。

例如:

小组成员: 查理,艾伦,布莱恩

不是小组成员: 本,克里斯,艾丽西亚......等等。

必需的QuerySet /选择字段排序: 艾伦,布莱恩,查理,艾丽西亚,本,克里斯......等等。

鉴于我正在使用Django 1.11,我想我会尝试新的.union ORM功能:

group_members = User.objects.filter(groups__name="AddEditCalibrations").order_by("first_name", "last_name")
everyone_else = User.objects.exclude(username__in="group_members").order_by("first_name", "last_name")
total_qs = group_members.union(everyone_else)

这只会引发错误:

DATABASE ERROR - ORDER BY not allowed in subqueries of compound statements

如果没有2个ORDER_BY,似乎工作正常,但是列表不是按字母顺序排序的,并且该组的成员需要位于选择的顶部(QS中的第一个)只是混合在一起与其他人一起。

所以我尝试了更简单的方法:

total_qs = User.objects.order_by(groups__name="AddEditCalibrations", "first_name", "last_name")

引发错误:

SyntaxError: positional argument follows keyword argument

修改

关注rajkris'建议使用itertools我试过:

total_list = list(chain(group_members, everyone_else))

鉴于我需要一个QS来填充选择字段,然后尝试将其转换回QS:

total_qs = User.objects.filter(username__in=total_list)

但是这只能得到一个未排序的列表,所有用户混合在一起而没有任何排序。

3 个答案:

答案 0 :(得分:3)

您无法通过具有该语法的条件进行排序。您可以按Newtonsoft.Json订购,但这不是您想要的。相反,您需要将其翻译成另一个可以订购的字段 - 让我们将其称为groups__name; weight应获得groups__name="AddEditCalibrations" weight,其他人的权重应为0

你可以试试这个:

1

如果from django.db.models import IntegerField, Value group_members = User.objects.filter(groups__name="AddEditCalibrations").annotate(weight=Value(0, IntegerField())) everyone_else = User.objects.exclude(groups__name="AddEditCalibrations").annotate(weight=Value(1, IntegerField())) total_qs = group_members.union(everyone_else).order_by("weight", "first_name", "last_name") 仍未按预期工作,则可能需要union

或者,一气呵成:

all=True

答案 1 :(得分:1)

也许尝试从itertools链转换成一个列表:

from itertools import chain
result_list = list(chain(group_members, everyone_else))

也许我们可以使用这样的东西来获取查询集。

pk_list = [each.id for each in result_list]
ordering = 'FIELD(`id`, %s)' % ','.join(str(id) for id in pk_list)
queryset = User.objects.filter(pk__in=pk_list).extra(
       select={'ordering': ordering}, order_by=('ordering',))

答案 2 :(得分:0)

你可以试试这样的吗?

group_members = User.objects.order_by("first_name", "last_name")
sorted_group_members = sorted(group_members.all(), reverse=True, key = lambda a: a.some_function_returning_the_user_group_of_each_user())