2017-05-30 65 views
2

我需要根据布尔字段对查询集的两个部分进行不同的排序。 - 具有bool_val == True的行应先到达,并按date_a排序,按升序排序。 - 具有bool_val == False的行应该排在第二位,并按date_b降序排序。如何在django中包含有条件的order_by?

我已经得到的最接近的是

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对象。

+0

您可以使用日期负号:'.order_by(“bool_val”,“-sort_order”)' –

+0

是的,但你不能有条件地做到这一点。 –

回答

2

这是结束了工作:

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

您应该可以使用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') 

有些麻烦......和潜在的慢。我没有测试此

+0

这是一个很好的想法,但是我试图用django解释'EPOCH FROM TIMESTAMP WITH TIME ZONE'作为字段名称时遇到了麻烦。我尝试了几个变种,但最终找到了一个更优雅的解决方案;看到我发布的答案。 –