Django Queryset - 按字母顺序排序然后将特定条目移至顶部
Django Queryset - Sort alphabetical then move specific entries to top
编辑(背景信息)
我创建了一个自定义的 UserModelChoiceField class,它接受用户的 QuerySet 并用他们的全名填充 select 字段:
class UserModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return "{} ({})".format(obj.get_full_name(), obj.username)
我现在正尝试使用此 class,例如:
calibrated_by_internal = UserModelChoiceField(
queryset=total_qs,
required=False,
widget=forms.Select(attrs={"class": "form-control"})
)
.
原问题
我正在尝试创建一个自定义排序的 QuerySet,它允许我在按字母顺序排序的表单上放置一个 User ModelChoice select 字段,但是特定权限组的 3 个成员被放置在顶.
例如:
群组成员:
查理、艾伦、布赖恩
不是群组成员:
本、克里斯、艾丽西亚……等等
必需的查询集/Select 字段排序:
Alan、Brian、Charlie、Alicia、Ben、Chris...等
鉴于我使用的是 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_BYs,它似乎工作正常,但是列表没有按字母顺序排序,并且该组的成员需要位于 select 的顶部(第一个在 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 来填充 select 字段,然后我尝试将其转换回 QS:
total_qs = User.objects.filter(username__in=total_list)
但这只给我一个未排序的列表,所有用户混合在一起,没有任何排序。
也许可以尝试使用 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',))
您不能使用该语法按条件排序。您可以按 groups__name
订购,但这不是您想要的。相反,您需要将它翻译成另一个您可以用来订购的字段——我们称它为 weight
; groups__name="AddEditCalibrations"
应该得到 0
的 weight
并且每个其他人应该得到 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
。
或者,一次性:
from django.db.models import Case, IntegerField, Value, When
total_qs = User.objects.annotate(weight=Case(
When(groups__name="AddEditCalibrations", then=Value(0)),
default=Value(1),
output_field=IntegerField()
)).order_by("weight", "first_name", "last_name")
你能试试这样的吗?
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())
编辑(背景信息)
我创建了一个自定义的 UserModelChoiceField class,它接受用户的 QuerySet 并用他们的全名填充 select 字段:
class UserModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return "{} ({})".format(obj.get_full_name(), obj.username)
我现在正尝试使用此 class,例如:
calibrated_by_internal = UserModelChoiceField(
queryset=total_qs,
required=False,
widget=forms.Select(attrs={"class": "form-control"})
)
.
原问题
我正在尝试创建一个自定义排序的 QuerySet,它允许我在按字母顺序排序的表单上放置一个 User ModelChoice select 字段,但是特定权限组的 3 个成员被放置在顶.
例如:
群组成员: 查理、艾伦、布赖恩
不是群组成员: 本、克里斯、艾丽西亚……等等
必需的查询集/Select 字段排序: Alan、Brian、Charlie、Alicia、Ben、Chris...等
鉴于我使用的是 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_BYs,它似乎工作正常,但是列表没有按字母顺序排序,并且该组的成员需要位于 select 的顶部(第一个在 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 来填充 select 字段,然后我尝试将其转换回 QS:
total_qs = User.objects.filter(username__in=total_list)
但这只给我一个未排序的列表,所有用户混合在一起,没有任何排序。
也许可以尝试使用 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',))
您不能使用该语法按条件排序。您可以按 groups__name
订购,但这不是您想要的。相反,您需要将它翻译成另一个您可以用来订购的字段——我们称它为 weight
; groups__name="AddEditCalibrations"
应该得到 0
的 weight
并且每个其他人应该得到 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
。
或者,一次性:
from django.db.models import Case, IntegerField, Value, When
total_qs = User.objects.annotate(weight=Case(
When(groups__name="AddEditCalibrations", then=Value(0)),
default=Value(1),
output_field=IntegerField()
)).order_by("weight", "first_name", "last_name")
你能试试这样的吗?
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())