2016-04-18 85 views
1

我填充下拉在我的models.py定義的值,是這樣的:如何根據自定義值列表來訂購Django查詢?

rank_choices = (
     ('King', 'King') 
     ('Prince', 'Prince') 
     ('Duke', 'Duke') 
     ('Baron', 'Baron') 
     ('Lackey', 'Lackey') 

當我在特定時間顯示的人的名單與排名,我希望他們能夠出現在降序排名。

當前查詢(只是字母順序):

attendees = Rank.objects.filter(feast__id=feast__id).exclude(rank='Lackey').order_by('rank') 

我應該如何改變這種通過在列表中的位置進行排序?

我正在考慮這個:

rank_choices = (
     (0, 'King') 
     (1, 'Prince') 
     (2, 'Duke') 
     (3, 'Baron') 
     (4, 'Lackey') 

但即使我能得到詳細值從數值後面,在訂購的任何改變都進行數據更改前返回不正確的值。有沒有更好的辦法?

選擇的解決方案

通過WIM的回答啓發,我最後做數據遷移改變行列數值和排序如下。

ranks = sorted(ranks, key=lambda x: int(x.rank)) 

我通過導入從我的模型rank_choices到我的意見,並與分選後的相應的職稱取代數值獲取詳細值回。

回答

2

它看起來像你有rank存儲在數據庫中的CharField。在不改變架構的情況下執行自定義order_by並不簡單。因此,對於簡單的情況下,你可以考慮進行排序在Python代碼:

rank_lookup = {rank: n for n,(rank,rank) in enumerate(rank_choices[::-1])} 
ranks = Rank.objects.filter(...).values_list('rank', flat=1) 
ranks = sorted(ranks, key=rank_lookup.get) 

調用sorted後,查詢集將被評估,你將有代替Python列表。

如果這不是令人滿意的,它是在Django的ORM可能(但不漂亮)使用Case/When結構,讓你在查詢集想要的結果:

>>> for rank, rank in rank_choices: 
...  Rank.objects.create(rank=rank) 
...  
>>> Rank.objects.order_by('rank') # alphabetical 
[<Rank: Baron>, <Rank: Duke>, <Rank: King>, <Rank: Lackey>, <Rank: Prince>] 
>>> rank_lookup = {rank: n for n,(rank,rank) in enumerate(rank_choices)} 
>>> cases = [When(rank=rank, then=Value(rank_lookup[rank])) for rank in rank_lookup] 
>>> cases 
[<When: WHEN <Q: (AND: ('rank', 'King'))> THEN Value(0)>, 
<When: WHEN <Q: (AND: ('rank', 'Lackey'))> THEN Value(4)>, 
<When: WHEN <Q: (AND: ('rank', 'Baron'))> THEN Value(3)>, 
<When: WHEN <Q: (AND: ('rank', 'Prince'))> THEN Value(1)>, 
<When: WHEN <Q: (AND: ('rank', 'Duke'))> THEN Value(2)>] 
>>> 
>>> Rank.objects.annotate(my_order=Case(*cases, output_field=IntegerField())).order_by('my_order') 
[<Rank: King>, <Rank: Prince>, <Rank: Duke>, <Rank: Baron>, <Rank: Lackey>] 
>>> Rank.objects.annotate(my_order=Case(*cases, output_field=IntegerField())).order_by('-my_order') 
[<Rank: Lackey>, <Rank: Baron>, <Rank: Duke>, <Rank: Prince>, <Rank: King>] 

由於用戶「畝是太短「在comments鼓舞這個想法。由於conditional expressions中的新功能,這將在Django 1.8+中運行。

對於不幸被卡住老版本的Django的用戶來說,通過使用Queryset.extra來構建一個原始的sql片段來傳遞相同的想法是可能的。

+0

謝謝。我要試試這個。我需要顯示Rank對象及其fk對象的一些其他屬性,但也許我可以省略flat = 1,將多個列賦給values_list並使用它。 –

+0

是的,只是在排序的調用中修改傳遞給'key'的參數 – wim

+0

不能用Django指定ORDER BY表達式嗎?例如,當'國王'的情況下按順序排列,然後當'王子'然後是1 ...結束時輸入0'。 SQL允許您通過任何有效的表達式進行排序,而不僅僅是列。 –