2017-05-30 70 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'作爲字段名稱時遇到了麻煩。我嘗試了幾個變種,但最終找到了一個更優雅的解決方案;看到我發佈的答案。 –