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

Posted

技术标签:

【中文标题】Django Queryset - 按字母顺序排序,然后将特定条目移到顶部【英文标题】:Django Queryset - Sort alphabetical then move specific entries to top 【发布时间】:2018-05-22 19:22:21 【问题描述】:

编辑(背景信息)

我创建了一个自定义 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 个成员放在顶部。

例如:

小组成员: 查理、艾伦、布赖恩

不是该组的成员: 本、克里斯、艾丽西亚……等等。

必需的查询集/选择字段排序: 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_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)

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

【问题讨论】:

您使用的排序(User.objects.sort())是什么?是你定义的管理器方法吗? 抱歉,从内存中复制代码时出现拼写错误(我以前使用的另一种语言挂断)我已经更新了问题,引发的错误消息仍然相同。 order_by 将不接受关键字参数。我认为您不能根据您的情况使用 order_by 【参考方案1】:

您不能使用该语法按条件排序。您可以通过groups__name 订购,但这不是您想要的。相反,您需要将其转换为您可以订购的另一个字段——我们称之为weightgroups__name="AddEditCalibrations" 应该得到 weight0 并且每个其他应该得到 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")

【讨论】:

@TimB 运气好吗? 抱歉,我花了这么长时间才回到这个问题上,工作中出现了一些紧急的事情,让我在几周内离开了开发。您编辑的第二个解决方案运行良好,我今天下午将其投入生产。非常感谢您的帮助! 实际上废弃了,似乎 AddEditCalibrations 组中的任何人都出现在 qs 中两次,一次在顶部,然后又在更下方。这意味着当其中一个选择其中一个时,我们得到了一个多功能的错误:get()返回多个用户 - 它返回2! span> 【参考方案2】:

也许可以尝试使用 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',))

【讨论】:

不幸的是,这会将整个 QS 按字母顺序排列,包括我需要位于顶部的两个用户(AddEditCalibrations 组的成员)。我想要的是该组的用户按字母顺序排序,然后在他们下面所有其他用户分别按字母顺序排序。对不起,如果我不够清楚! 我认为在这种情况下我们不能使用联合。也许使用 itertool 链来附加两个有序的查询集。更新了答案 我按照您的建议进行了尝试,得到了一个排序列表,但是当我将其转换回 QS 时,它再次变得未排序!我已更新问题以反映这一点。 我认为您不需要 qs 来填充选择字段,列表应该足以迭代以填充选择字段。请确认结果列表是否按预期排序。 我试过了,得到一个 AttributeError - 'list' object has no attribute 'all'【参考方案3】:

你能试试这样的吗?

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

【讨论】:

以上是关于Django Queryset - 按字母顺序排序,然后将特定条目移到顶部的主要内容,如果未能解决你的问题,请参考以下文章

Django QuerySet按表达式排序[重复]

按模型的属性(不是字段)对 Django QuerySet 进行排序

按日期时间的月/日订购 Django QuerySet?

在 django(postgres 后端)中按数字字符串排序查询结果

excel中怎么将英文单词按字母顺序排序?

Django - 如何在页面上更改搜索结果的排序顺序?