2013-03-26 32 views
4

我想弄清楚一個棘手的Django查詢,我希望你能幫上忙。我有這樣的模式:如何返回表格中最受歡迎的物品,但每個物品的唯一位置?

class ActiveVenue(models.Model): 
    event = models.ForeignKey(Event) 
    venue = models.ForeignKey(Venue) 

class Venue(models.Model): 
    name = models.CharField(max_length=200) 

class Event(models.Model): 
    user = models.ForeignKey(User) 
    name = models.CharField(max_length=200) 

在我的應用程序有很多活動和每個事件可以有多個活動場地,因此我目前的數據結構。如果您的解決方案不是「將您的模型更改爲foo」,而是已經部署的網站,我希望保留當前的模型結構。

我想編寫一個查詢,返回最受歡迎的場地,但每個用戶只會計算一次場地。例如,如果我有一個用戶有四個活動,並且他們每次都使用相同的場地,我只想在確定最受歡迎的場地時統計一次場地。

爲了說明這一點,想象這是我的數據:

event: A user: bob venue: The Hill 
event: B user: bob venue: The Hill 
event: C user: bob venue: The Hill 
event: D user: jane venue: The Oaks 
event: E user: sarah venue: The Pound 
event: F user: david venue: The Pound 
event: G user: ron venue: The Oaks 
event: H user: erica venue: The Oaks 

這裏流行的順序將是:

1. The Oaks 
2. The Pound 
3. The Hill 

任何建議我如何編寫一個查詢來做到這一點,雙方的Postgres工作和sqlite(換句話說,不依賴於distinct()(在sqlite中不支持))?

謝謝!

+0

我很確定'Disctinct'應該在sqlite中工作。按照https://docs.djangoproject.com/en/dev/ref/models/querysets/#distinct,你不能在sqlite中指定參數,但該函數應該可以工作。 – miki725 2013-03-26 05:33:47

+0

考慮到用戶限制,我不認爲你可以使用Django ORM來做到這一點。爲此,您似乎必須編寫一些手動SQL。 – miki725 2013-03-26 05:35:27

+0

@ miki725我同意,如果它被抽象爲一個返回集合的函數,它可以在支持distinct()的情況下推遲到ORM,否則手動SQL。 – 2013-03-26 22:15:55

回答

1

這是行不通的嗎?

from collections import Counter 
results = Counter([vid for vid, eid in ActiveVenue.objects.values_list("venue_id", "event_id").distinct()] 
+0

+1,collections.Counter質量非常高,專爲此目的而設計。在獲得了上面的代碼之後,諸如'results.most_common(3)'之類的東西應該給你你正在尋找的答案,很好的排序和一切。 – robru 2013-03-26 06:21:18

+0

雖然這個工作,我認爲我們應該避免在Python中迭代整個查詢集。僅在切片(分頁)後迭代。如果我們迭代原始查詢集而不先切片,則查詢集可能非常大,並且視圖可能需要很長時間才能做出響應。 – 2013-03-26 06:32:17

0

看看是否有這樣的工作

ActiveVenue.objects.all().annotate(score=Count('event__user', distinct=True)).order_by('-score') 
0

這是我的版本(拋光試驗):

models.py

class Venue(models.Model): 
    name = models.CharField(max_length=200) 

    def __unicode__(self): 
     return self.name 

    def ranking(self): 
     count=0 
     actives = ActiveVenue.objects.filter(
      venue__name=self.name).values(
      'event__user__username', 'venue__name').distinct() 
     for active in actives: 
      count += 1 
     return count 

views.py

def getRanking(anObject): 
    return anObject.ranking() 

def myview(request): 
    venues = list(Venue.objects.filter()) 
    venues.sort(key=getRanking, reverse=True) 
    return render(request,'page.html',{'venues': venues}) 

模板

{% for venue in venues %} 
    {{forloop.counter}}. {{venue}}<br/> 
{% endfor %} 

輸出:

  1. 奧克斯
  2. 英鎊兌美元
  3. 希爾
+0

再次,這將所有內容加載到內存中,並將整個表格排序在內存中。不是一個好習慣。如果某個SQL查詢無法實現,那麼應該對該模式進行非規範化處理,使其成爲可能,而不是在框架代碼中加載所有內容並進行排序/過濾。 – 2013-03-26 08:01:21

+0

@OwaisLone這隻會按照他的要求對活動場地進行排序,但我並沒有把它放在過濾器中。 – catherine 2013-03-26 08:15:27

+0

我不是說它爲喬諾工作。真的取決於他將擁有多少ActiveVenues。只是指出一個好的做法。 – 2013-03-26 08:25:15

0

這是我的問題。首先,這是一個查詢,它將獲取與相關用戶相對應的Venue ID和分數。

testquery = ActiveVenue.objects.values("venue").annotate(score=Count("event__user", distinct=True)).order_by("-score") 

結果

[{'score': 3, 'venue': 2}, {'score': 2, 'venue': 3}, {'score': 1, 'venue': 1}] 

接下來,會場ID將被放置在一個新的列表。

query_ids = [item["venue"] for item in testquery] 
[2, 3, 1] 

接下來,必須獲得相應的Venue對象。

tempresult = Venue.objects.filter(id__in=query_ids) 
[<Venue: The Hill>, <Venue: The Oaks>, <Venue: The Pound> 

最後一個列表解析會進行重新排序基於先前獲得的分數場館對象。

result = [venue for venue_id in query_ids for venue in tempresult if venue_id == venue.id] 
[<Venue: The Oaks>, <Venue: The Pound>, <Venue: The Hill>] 

這給出了基於測試數據的正確結果。

相關問題