Django order_by specific order

kuuvgm7e  于 2023-10-21  发布在  Go
关注(0)|答案(4)|浏览(118)

是否可以在django ORM中复制这种特定的SQL排序:

order by

(case

    when id = 5 then 1

    when id = 2 then 2

    when id = 3 then 3

    when id = 1 then 4

    when id = 4 then 5

end) asc

uqcuzwp8

uqcuzwp81#

从Django 1.8开始,你有了Conditional Expressions,所以不再需要使用extra

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

SomeModel.objects.annotate(
    custom_order=Case(
        When(id=5, then=Value(1)),
        When(id=2, then=Value(2)),
        When(id=3, then=Value(3)),
        When(id=1, then=Value(4)),
        When(id=4, then=Value(5)),
        output_field=IntegerField(),
    )
).order_by('custom_order')
htrmnn0y

htrmnn0y2#

这是可能的。从Django 1.8开始,你可以通过以下方式来做:

from django.db.models import Case, When

ids = [5, 2, 3, 1, 4]
order = Case(*[When(id=id, then=pos) for pos, id in enumerate(ids)])
queryset = MyModel.objects.filter(id__in=ids).order_by(order)
wsxa1bj1

wsxa1bj13#

你可以用extra()或更简单的raw()来做,但它们在更复杂的情况下不能很好地工作。

qs.extra(select={'o':'(case when id=5 then 1 when id=2 then 2 when id=3 then 3 when id=1 then 4 when id=4 then 5 end)', order_by='o'}

YourModel.raw('select ... order by (case ...)')

对于你的代码,条件集是非常有限的,你可以很容易地在Python中排序。

h7appiyu

h7appiyu4#

我回答这个老问题,分享Django的演变
从Django 3.2开始,有一个alias()非常适合这种情况:
与annotate()相同,但不是注解QuerySet中的对象,而是保存表达式以供以后与其他QuerySet方法重用。当不需要表达式本身的结果,但将其用于筛选、排序或作为复杂表达式的一部分时,这很有用。不选择未使用的值可以从数据库中删除冗余工作,从而提高性能。

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

class MyObject(models.Model):
    priority = models.CharField(max_length=10)

priority_order = Case(
    When(priority='high', then=Value(1)),
    When(priority='medium', then=Value(2)),
    When(priority='low', then=Value(3)),
)
MyObject.objects.alias(priority_order=priority_order).order_by("priority_order")

根据Django文档,**alias()似乎比annotate()order_by()**中的直接表达式更好:
filter()和order_by()可以直接接受表达式,但表达式的构造和使用通常不在同一个地方发生(例如,QuerySet方法创建表达式,供以后在视图中使用)。alias()允许增量地构建复杂的表达式,可能跨越多个方法和模块,通过别名引用表达式部分,并仅使用annotate()获得最终结果。

相关问题