2010-05-16 41 views
3

這裏是我的模型:Django的左連接M2M領域

class User(models.Model): 
    pass 

class Item(models.Model): 
    pass 

class ItemVote(models.Model): 
    user = models.ForeignKey(User) 
    item = models.ForeignKey(Item) 
    vote = models.BooleanField() 

我想要檢索的項目清單,我想知道,如果當前用戶投票爲每個項目。如何改變我的查詢對象,這樣它會生成SQL類似於:

SELECT ... 
FROM items 
LEFT OUTER JOIN item_votes ON (item_votes.user_id = ? AND 
           item_votes.item_id = items.id) 

回答

1

你不能在普通的Django的查詢。這是一個多對多的關係船,你甚至可以更清楚地做這樣的規定是:

class Item(models.Model): 
    votes = models.ManyToManyField(User, through='ItemVote', related='votedItems') 

在一個多對多的關係,我們可以說的相關集(因爲有多個對象)。雖然Django可以過濾相關設置,例如:

Item.objects.filter(votes=request.user) 

這將返回所有用戶已投票的項目。但是,當您在這些對象中列出.votes屬性時,您將獲得全部已投票支持該項目的用戶。過濾用於原始對象,從不用於相關集合。

在純粹的Django的方式,可以擴大我以前的項目類:

class Item(models.Model): 
    votes = models.ManyToManyField(User, through='ItemVote', related='votedItems') 

    def markVoted(self, user): 
     self.voted = user in self.votes 

然後調用每個對象此方法。然而,這將會爲每個對象的投票創建額外的查詢(並且不,select_related不適用於多對多關係)。

解決這個問題的唯一辦法就是一些SQL添加到您的查詢集使用extra method,像這樣:

Item.objects.extra('voted' : 'SELECT COUNT(*) FROM app_itemvote WHERE app_itemvote.item_id = app_item.id AND app_itemvote.user_id=%d'%request.user.pk) 
+1

也許我錯過了一些東西,但是這似乎是Django功能上的一個巨大缺口。這個問題似乎是非常普遍的。我無法想象那裏已經沒有好的解決方案了。在1.2中,我可以使用.raw方法並直接寫SQL,但是你會放鬆len()和slices,這意味着你必須手動計算分頁。 – limscoder 2010-05-17 17:17:57

+0

是的,這是一個常見的問題(有人問了幾次)。然而,大多數情況下,人們不會爲「django」方式的性能打擊而煩惱。最大的問題在於事實上,django不包含查詢集中的相關集合,所以這是相當複雜的附加行爲。我會說:看在django開發網站上是否有關於它的票,如果是這樣的話:評論它或訂閱它。如果沒有,只需製作一個,誰知道功能將在未來的某個時間添加:)。包括過濾相關集在許多方面將是有用的:-)。 – KillianDS 2010-05-21 14:01:23

0

返回ItemVote項目:

items = ItemVote.objects.filter(user=user) 

使用Django的模板:

{% for i in items %} 
item: {{ i.item }} 
voted: {{ i.voted }} 
{% endfor %}