2009-08-28 112 views
1

我打算髮布一些不完整的代碼以使示例變得簡單。我正在運行遞歸函數來計算層次結構上的一些度量標準。緩存查詢集和重新評估

類類別(models.Model):

parent = models.ForeignKey('self', null=True, blank=True, related_name='children', default=1) 

def compute_metrics(self, shop_object, metric_queryset=None, rating_queryset=None) 
    if(metric_queryset == None): 
     metric_queryset = Metric.objects.all() 
    if(rating_queryset == None): 
     rating_queryset = Rating.objects.filter(shop_object=shop_object) 

    for child in self.children.all(): 
     do stuff 
     child_score = child.compute_metrics(shop_object, metric_queryset, rating_queryset) 

    metrics_in_cat = metric_queryset.filter(category=self) 
    for metric in metrics_in_cat 
      do stuff 

我希望這是足夠的代碼,看看發生了什麼事情。我在這裏之後是一個遞歸函數,它將只運行一次這些查詢,然後將結果傳遞給下一個。這似乎並不是現在正在發生的事情,它正在扼殺性能。是PHP/MySQL(就像我在使用Django後不喜歡它們一樣!)我可以只運行一次查詢並將它們傳遞給它們。

從我所瞭解的Django的查詢集中,他們不會在我的if queryset == None中進行評估,然後queryset = stuff部分。我如何強制這個?當我做像metric_queryset.filter(category=self)這樣的事情時,它會被重新評估嗎?

我不關心數據的新鮮度。我只想從DB讀取一次度量和評級,然後過濾它們而不再次敲擊DB。這是一個令人沮喪的問題,感覺應該有一個非常簡單的答案。 Pickling看起來可以工作,但在Django文檔中沒有很好解釋。

回答

3

我認爲這裏的問題是你直到遞歸調用之後才評估queryset。如果您使用list()強制對查詢集進行評估,那麼它只應該擊中數據庫一次。請注意,您必須將metrics_in_cat行更改爲python級別篩選器,而不是使用queryset篩選器。

parent = models.ForeignKey('self', null=True, blank=True, related_name='children', default=1) 

def compute_metrics(self, shop_object, metric_queryset=None, rating_queryset=None) 
    if(metric_queryset is None): 
     metric_queryset = list([Metric.objects.all()) 
    if(rating_queryset is None): 
     rating_queryset = list(Rating.objects.filter(shop_object=shop_object)) 

    for child in self.children.all(): 
     # do stuff 
     child_score = child.compute_metrics(shop_object, metric_queryset, rating_queryset) 

    # metrics_in_cat = metric_queryset.filter(category=self) 
    metrics_in_cat = [m for m in metric_queryset if m.category==self] 
    for metric in metrics_in_cat 
     # do stuff