如何根据模型外字段值按特定顺序订购Django QuerySet



我有以下(简化)模型:

class MyModel(models.Model):
    TYPE_SOMETHING = 0
    TYPE_SOMETHING_ELSE = 1
    TYPE_ANOTHER = 2
    type = models.SmallIntegerField()
    # other model fields

我想执行一个查询,例如:

MyModel.objects.all()

但是,QuerySet必须按照type属性进行排序,但要按照特定的顺序:

1. TYPE_SOMETHING_ELSE
2. TYPE_SOMETHING
3. TYPE_ANOTHER

这不是升序或降序。

我该怎么做?

我们可以在djangoSortedDict的帮助下对queryset进行排序有关SortedDict的更多信息,请浏览此文档

from django.utils.datastructures import SortedDict
MyModel.objects.extra( select=SortedDict([('type',
  'case when type = %d then 2 when type = %d then 1 else 0  end' %
  (MyModel.TYPE_SOMETHING_ELSE, MyModel.TYPE_SOMETHING, MyModel.TYPE_ANOTHER))]) )
  .order_by('type')

我发布了一个小型第三方,它通过为您生成CASE WHEN ... SQL语句来帮助解决此问题。

有了这个库,你可以做:

from django_reorder.reorder import reorder
MyModel.objects.order_by(reorder(type=[TYPE_SOMETHING_ELSE, TYPE_SOMETHING, TYPE_ANOTHER]))

您的查询集将按照您想要的方式进行排序。

我认为你可以这样做:

from django.utils.datastructures import SortedDict

MyModel.objects.extra(select=SortedDict([('types',
'case when type = %d then 2 when type = %d then 1 else 0 end' % 
(MyModel.TYPE_SOMETHING_ELSE,MyModel.TYPE_SOMETHING))])
).order_by('types')

这背后的sql查询是

SELECT (case when type = 1 then 2 when type = 0 then 1 else 0  end) AS "type", "testapp_mymodel"."id", "testapp_mymodel"."types" FROM "testapp_mymodel" ORDER BY "types" ASC

SortedDict只用于保留选择选项的顺序

您可以将order_by()方法与Case()表达式一起使用:

from django.db.models import Case, When, IntegerField    
queryset = MyModel.objects.order_by(Case(
    When(type=MyModel.TYPE_ANOTHER, then=0),
    When(type=MyModel.TYPE_SOMETHING, then=1),
    When(type=MyModel.TYPE_SOMETHING_ELSE, then=2),
    output_field=IntegerField()))

这将是一个比使用extra()和原始SQL更好的选择,Django文档建议不要使用原始SQL。

如果我能正确理解你的问题,你想存储一组整数,但要以非标准的顺序从数据库中检索它们。我不确定常量变量到底是如何融入模式的,但我的建议是,应该将变量名单独存储在一个列表中,以便通过它们的索引访问,从而将变量名排除在外。例如:

choices_list = [TYPE_SOMETHING, TYPE_SOMETHING_ELSE, TYPE_ANOTHER]
TYPE_CHOICES = (
    (0, 1),
    (1, 0),
    (2, 3)
)
class MyModel(models.Model):
    type = models.SmallIntegerField(choices=TYPE_CHOICES)
    def get_type_display(self):
        return choices_list[self.type.id]

如果您想更进一步,可以覆盖SmallIntegerField以自动返回这些值。

由于Django的SortedDict已被弃用,我使用了与Annop和Savad KP:类似的答案

import collections
MyModel.objects.extra(
    select=collections.SortedDict([(
        'types',
        'CASE WHEN type = %d THEN 0 ',
        'WHEN type = %d THEN 1',
        'WHEN type = %s THEN 2',
        'END' % (
            MyModel.TYPE_ANOTHER,
            MyModel.TYPE_SOMETHING,
            MyModel.TYPE_SOMETHING_ELSE
        ))
    ])
).order_by('types')

最后很简单:)

最新更新