如何针对自定义值列表订购Django查询?

时间:2016-04-18 20:22:21

标签: python django postgresql django-queryset

我正在使用models.py中定义的值填充下拉列表,例如:

    rank_choices = (
        ('King', 'King')
        ('Prince', 'Prince')
        ('Duke', 'Duke')
        ('Baron', 'Baron')
        ('Lackey', 'Lackey')

当我显示特定时间排名的人员列表时,我希望他们以降序排名。

当前查询(仅按字母顺序排列):

attendees = Rank.objects.filter(feast__id=feast__id).exclude(rank='Lackey').order_by('rank')

如何根据列表中的位置更改此顺序?

我正在考虑这个:

    rank_choices = (
        (0, 'King')
        (1, 'Prince')
        (2, 'Duke')
        (3, 'Baron')
        (4, 'Lackey')

但即使我可以从数值中获取详细值,排序中的任何更改都会返回不正确的数据预更改值。还有更好的方法吗?

选择解决方案

受到wim的回答的启发,我最终进行了数据迁移,将排名更改为数值并按如下方式进行排序。

ranks = sorted(ranks, key=lambda x: int(x.rank))

我通过将我的模型中的rank_choices导入到我的视图中并在排序后用相应的标题替换数值来获取详细值。

1 个答案:

答案 0 :(得分:2)

看起来你有rank存储在数据库中的CharField中。在不更改架构的情况下执行自定义order_by并不简单。因此,对于简单的情况,您可以考虑在python代码中进行排序:

rank_lookup = {rank: n for n,(rank,rank) in enumerate(rank_choices[::-1])}
ranks = Rank.objects.filter(...).values_list('rank', flat=1)
ranks = sorted(ranks, key=rank_lookup.get)

在调用sorted之后,将评估查询集,您将拥有一个python列表。

如果这不令人满意,Django ORM中可能(但不是很漂亮)可以使用Case / When构造在查询集中获得所需的结果:

>>> for rank, rank in rank_choices:
...     Rank.objects.create(rank=rank)
...     
>>> Rank.objects.order_by('rank')  # alphabetical
[<Rank: Baron>, <Rank: Duke>, <Rank: King>, <Rank: Lackey>, <Rank: Prince>]
>>> rank_lookup = {rank: n for n,(rank,rank) in enumerate(rank_choices)}
>>> cases = [When(rank=rank, then=Value(rank_lookup[rank])) for rank in rank_lookup]
>>> cases
[<When: WHEN <Q: (AND: ('rank', 'King'))> THEN Value(0)>,
 <When: WHEN <Q: (AND: ('rank', 'Lackey'))> THEN Value(4)>,
 <When: WHEN <Q: (AND: ('rank', 'Baron'))> THEN Value(3)>,
 <When: WHEN <Q: (AND: ('rank', 'Prince'))> THEN Value(1)>,
 <When: WHEN <Q: (AND: ('rank', 'Duke'))> THEN Value(2)>]
>>>
>>> Rank.objects.annotate(my_order=Case(*cases, output_field=IntegerField())).order_by('my_order')
[<Rank: King>, <Rank: Prince>, <Rank: Duke>, <Rank: Baron>, <Rank: Lackey>]
>>> Rank.objects.annotate(my_order=Case(*cases, output_field=IntegerField())).order_by('-my_order')
[<Rank: Lackey>, <Rank: Baron>, <Rank: Duke>, <Rank: Prince>, <Rank: King>]

感谢comments中的用户“mu太短”以激发这一想法。由于conditional expressions中的新功能,这将适用于Django 1.8+。

对于不幸被困在较旧版本的Django上的用户来说,通过使用Queryset.extra构建一个原始sql片段来传递相同的想法是可能的。