2010-11-26 15 views
5

在以下示例中,當調用數據庫昂貴的屬性(示例中爲related_spam)時,將使用cached_attr來獲取或設置模型實例的屬性。在這個例子中,我使用cached_spam來保存查詢。我在設置和獲取值時會打印報表,以便我可以對其進行測試。我在一個視圖中通過將Egg實例傳遞到視圖中並在視圖中使用{{ egg.cached_spam }}以及Egg模型上的其他方法來自己調用cached_spam來測試它。當我完成並測試了Django的開發服務器中的shell輸出後,發現屬性緩存被錯過了好幾次,並且成功獲得了好幾次。這似乎不一致。使用相同的數據,當我做出很小的更改(與更改打印語句的字符串一樣小)並刷新(使用所有相同的數據)時,會發生不同數量的錯過/成功。這是怎麼發生的呢?這段代碼是不正確的還是很成問題的?Python - 緩存屬性以避免將來計算

class Egg(models.Model): 
    ... fields 

    @property 
    def related_spam(self): 
     # Each time this property is called the database is queried (expected). 
     return Spam.objects.filter(egg=self).all() # Spam has foreign key to Egg. 

    @property 
    def cached_spam(self): 
     # This should call self.related_spam the first time, and then return 
     # cached results every time after that. 
     return self.cached_attr('related_spam') 

    def cached_attr(self, attr): 
     """This method (normally attached via an abstract base class, but put 
     directly on the model for this example) attempts to return a cached 
     version of a requested attribute, and calls the actual attribute when 
     the cached version isn't available.""" 
     try: 
      value = getattr(self, '_p_cache_{0}'.format(attr)) 
      print('GETTING - {0}'.format(value)) 
     except AttributeError: 
      value = getattr(self, attr) 
      print('SETTING - {0}'.format(value)) 
      setattr(self, '_p_cache_{0}'.format(attr), value) 
     return value 
+1

我想到了這個煩惱,並意識到解決這個問題的另一種方法是使用`with`模板標籤爲計算結果創建一個別名。 – nedned 2011-05-11 04:16:12

+0

@humble - thx,這實際上是一個相當有用的筆記。 – orokusaki 2011-05-12 19:44:44

回答

9

就你的代碼而言,沒有問題。這個問題可能不存在,但在你如何使用該代碼。

要認識到的主要問題是模型實例沒有身份。這意味着如果你在某個地方實例化一個Egg對象,並且在別的地方實例化一個Egg對象,即使它們引用相同的底層數據庫行,它們也不會共享內部狀態。所以在一個上調用cached_attr不會導致緩存被填充到另一箇中。

例如,假設你有一個relatedObject將類與一個ForeignKey雞蛋:

my_first_egg = Egg.objects.get(pk=1) 
my_related_object = RelatedObject.objects.get(egg__pk=1) 
my_second_egg = my_related_object.egg 

這裏my_first_eggmy_second_egg都引用數據庫行與PK 1,但他們相同的對象:

>>> my_first_egg.pk == my_second_egg.pk 
True 
>>> my_first_egg is my_second_egg 
False 

因此,填充上my_first_egg緩存不往裏面上my_second_egg

而且,當然,對象不會跨越請求持續存在(除非它們是專門製作成全局的,這太可怕了),所以緩存也不會持續存在。

+0

謝謝。提供的例子是,我想,到底發生了什麼。我似乎無法弄清楚我的代碼在哪裏,我得到這麼多的實例。基於我對代碼的審查(一個來自`my_related_object.egg`)的應該只有2個獨立實例的對象有4個實例。我認爲是時候徹底改革我的模型了;全部200行第8行( – orokusaki 2010-11-27 19:17:06

1

擴展的Http服務器是無共享的;你不能依賴任何單身。要共享狀態,您需要連接到特殊用途的服務。

Django的caching support適合您的使用情況。它也不一定是全球性的單身人士;如果你使用locmem://,它將是流程本地的,這可能是更有效的選擇。