如何在 django 中包含条件 order_by?
Posted
技术标签:
【中文标题】如何在 django 中包含条件 order_by?【英文标题】:How can I include a conditional order_by in django? 【发布时间】:2017-10-31 12:55:56 【问题描述】:我需要根据布尔字段对查询集的两个部分进行不同的排序。 - 具有 bool_val == True 的行应该排在第一位,并按 date_a 升序排序。 - bool_val == False 的行应该排在第二位,并按 date_b 降序排列。
我得到的最接近的是
MyModel.objects.all().annotate(
sort_order=Case(
When(bool_val=True, then=('date_a')),
default='date_b')
).order_by('-bool_val', 'sort_order')
但这会以相同的顺序对它们进行排序。如果我需要排序的值是数字,那么我会在注释中将一组乘以 -1,但它们是日期值,所以这不起作用。
我还研究过创建两个单独的查询集并将它们组合,但 union() 直到 1.11 才可用,我必须支持 1.10,并且我发现的其他组合查询集的方法不保留订购或不产生查询集(或两者)。 (我不清楚 union() 是否保持顺序,但它没有实际意义,所以我也没有深入研究它。)这必须是一个 django QuerySet 对象。
【问题讨论】:
您可以在日期上使用负号:.order_by('bool_val', '-sort_order')
可以,但不能有条件地这样做。
【参考方案1】:
这就是最终的工作:
MyModel.objects.annotate(
sort_order_true=Case(
When(bool_val=True, then=('date_a')),
default=None
),
sort_order_false=Case(
When(bool_val=False, then=('date_b')),
default=None
)
).order_by(
'sort_order_true',
'-sort_order_false',
)
【讨论】:
【参考方案2】:您应该能够使用Func
表达式将您的日期时间转换为时间戳,以便您可以否定它们。
对于 mysql,这是 UNIX_TIMESTAMP
函数
MyModel.objects.annotate(
sort_order=Case(
When(bool_val=True, then=Func(F('date_a'), function='UNIX_TIMESTAMP')),
When(bool_val=False, then=Value(0) - Func(F('date_b'), function='UNIX_TIMESTAMP')),
)
).order_by('-bool_val', 'sort_order')
对于 PostgreSQL,这是EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE '<datetime>')
MyModel.objects.annotate(
sort_order=Case(
When(bool_val=True, then=Func(F('date_a'), function='UNIX_TIMESTAMP')),
When(bool_val=False, then=Value(0) - Func('EPOCH FROM TIMESTAMP WITH TIME ZONE', F('date_b'), function='EXTRACT', arg_joiner=' ')),
)
).order_by('-bool_val', 'sort_order')
有点麻烦......而且可能很慢。我没有测试过这个
【讨论】:
这是个好主意,但我遇到了 django 试图将'EPOCH FROM TIMESTAMP WITH TIME ZONE'
解释为字段名称的麻烦。我尝试了几个变体,但最终找到了一个更优雅的解决方案;查看我发布的答案。
感谢您提出此解决方案。我和@CharlotteMays 遇到了同样的问题,我最终使用了这个:Extract('date_a', 'epoch')
代替了Func(F('date_a'), function='UNIX_TIMESTAMP'))
以上是关于如何在 django 中包含条件 order_by?的主要内容,如果未能解决你的问题,请参考以下文章
Django - 如何在序列化的 QuerySet 中包含带注释的结果?