2012-01-06 77 views
1

假設我有以下型號:添加額外的多對多領域Django的查詢集

class Thing(models.Model): 
    name = models.CharField(max_length=100) 
    ratings = models.ManyToManyField('auth.User', through='Rating') 

class Rating(models.Model): 
    user = models.ForeignKey('auth.User') 
    thing = models.ForeignKey('Thing') 
    rating = models.IntegerField() 

所以我有很多事情,每個用戶可以給每一個事情。我也有一個視圖,顯示所有事物的列表(並且它們數量巨大),用戶分配給他們每個人的評分。我需要一種方法來檢索數據庫中的所有數據:帶有額外字段user_rating的事物最多取自一個(因爲我們有固定用戶)相關評級對象。

平凡解看起來像這樣:

things = Thing.objects.all() 
for thing in things: 
    try: 
     thing.user_rating = thing.ratings.objects.get(user=request.user).rating 
    except Rating.DoesNotExist: 
     thing.user_rating = None 

但是,這種方法的缺陷是顯而易見的:如果我們有500分的事情,我們會做501個請求數據庫。每頁。每個用戶。這是該網站瀏覽次數最多的頁面。這個任務很容易用SQL JOIN解決,但實際上我有更復雜的模式,我肯定會受益於Django模型框架。所以問題是:是否可以這樣做Django方式?如果不是,考慮到這些任務非常普遍,這將是非常奇怪的。

據我所知,annotate()select_related()都不會幫助我。

回答

0

由於您打算在一頁中顯示所有內容。我可以想到這種方法。您可以試試這個:

獲取當前用戶提供的所有評級並獲取所有內容。

現在嘗試創建一個字典是這樣的:

thing_dict = {} 

for thing in Thing.objects.all(): 
    thing_dict[thing] = None 
for rating in Rating.objects.filter(user = request.user): 
    thing_dict[rating.thing] = rating 

現在thing_dict包含模型事情的所有條目作爲鍵,並具有其評級作爲其值。

可能不是最好的方法。我很想看到別人的回答。

+0

您仍然得到每個用戶的查詢,而不是聚合。 – Tom 2012-01-06 17:33:58

+0

好的,但它絕對不是完美的。如果事情分成頁面怎麼辦?比如說,有10000個東西,頁面包含100個條目。第二個循環將不得不查看所有可能的10000個評分。 – user285176 2012-01-06 19:03:10

+0

@Tom至少有2個查詢,而不是n + 1 – user285176 2012-01-06 19:05:18

3

我想你應該試試這個: https://docs.djangoproject.com/en/1.3/ref/models/querysets/#extra

result = Thing.objects.all().extra(select={'rating': 'select rating from ratings where thing_id = id'}) 

結果集中得到一個新的領域「評級」每個「東西」的對象。

我在最近的一個項目中使用這種方法。它生成一個複雜的查詢,而不是n + 1個查詢。

希望這會有所幫助:)

+0

看起來好多了,謝謝。它會執行嵌套的SELECT,我理解正確嗎?所以基本上數據庫執行相同數量的工作,並且JOIN仍然是可取的。 – user285176 2012-01-07 07:42:39

+1

你可以通過打印connection.queries忽略查詢,還有另一種方法,你可以寫一個自定義的原始sql查詢(使用連接。光標) – 2012-01-07 09:43:32

+0

作業的數量是不同的,一個事務,一個數據庫連接,一個嵌套查詢(而不是n + 1個查詢),它的速度相對較快 – 2012-01-07 09:50:14