如何在 Django 中包含条件 order_by?

How can I include a conditional order_by in django?

我需要根据布尔字段对查询集的两个部分进行不同的排序。 - 具有 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 对象。

您应该能够使用 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')

有点麻烦...而且可能很慢。我没有测试过这个

这就是最终的效果:

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',
)