以任意顺序对查询集进行排序

时间:2018-07-11 16:13:26

标签: python django

我有这样一个模板来显示数据库中的块

<div class="panel panel-default">
  <div class="panel-heading">
    <a style='font-size:18pt' href="article/list/{{ b.id }}">{{ b.name }}</a>
    <span class='pull-right'>{{ b.admin }}</span>
  </div>
  <div class="panel-body">
    {{ b.desc }}
  </div>
</div>

数据模型:

class Block(models.Model):
    STATUS = (
        (1,  'normal'),
        (0, 'deleted'),
    )
    name = models.CharField("block name", max_length=100)
    desc = models.CharField("block description", max_length=100)
    admin = models.CharField("block admin", max_length=100)
    status = models.IntegerField(choices=STATUS)

    class Meta:
        ordering = ("id",)

    def __str__(self):
        return self.name   

我将数据检索为

In [2]: from article.models import Block
In [3]: blocks = Block.objects.all()
In [4]: blocks
Out[4]: <QuerySet [<Block: Concepts>, <Block: Reading>, <Block: Coding>, <Block: Action>]>

我想以['concept','code', 'read', 'action']的任意顺序显示数据,而不是按ID

通过观察,我发现可以通过订购第二个字母来实现

In [7]: sorted(l, key=lambda item: item[1], reverse=True)
Out[7]: ['concept', 'code', 'read', 'action']

如何以这种方式对查询集进行排序?

2 个答案:

答案 0 :(得分:3)

您可以先为每个Block实例添加注释,然后按使用Substr function [Django-doc]的注释进行排序,例如:

from django.db.models.functions import Substr

Block.objects.annotate(
    sndchar=Substr('name', 2, 1)
).order_by('-sndchar')

作为每个Block实例的额外奖励,将具有一个sndchar属性,该属性是name属性的第二个字符(仅在此查询集中)。但这并不是我认为的真正问题。如果它与另一列发生冲突,则可以对其进行重命名。

-sndchar中的减号表示我们将以降序排序。如果要按升序排序,可以删除它。

因此,这里的排序是在数据库级别进行的,通常(显着)比在Django级别进行排序。

您还可以从第二个字符开始对字符串开头进行排序(这样,在使用 tie 的情况下,将考虑第三个字符等),通过删除length参数,然后用除第一个字符之外的名称进行注释:

from django.db.models.functions import Substr

# This will sort on the third character in case of a tie, and so on
Block.objects.annotate(
    sndchar=Substr('name', 2)
).order_by('-sndchar')

答案 1 :(得分:1)

使用Django的Conditional Expressions

from django.db.models import Case, IntegerField, Value, When

array = ['concept', 'code', 'read', 'action']

Block.objects.annotate(
    rank=Case(
        *[When(name=name, then=Value(array.index(name))) for name in array],
        default=Value(len(array)),
        output_field=IntegerField(),
    ),
).order_by('rank')