2012-06-07 50 views
7

我已使用MiddleWare的UpdateCacheMiddlewareFetchFromCacheMiddleware來啓用站點範圍內的匿名緩存以獲得不同級別的成功。用於繼承Django UpdateCacheMiddleware和FetchFromCacheMiddleware的技術

最大的問題是中間件只緩存匿名用戶的第一個請求。由於在該第一個響應中設置了session_id cookie,因此該視圖級別緩存因標題而異,所以該匿名用戶的後續請求不會擊中緩存。

我的網頁在匿名用戶中並沒有多大意義,只要他們有所不同,我可以通過Ajax處理。因此,我決定嘗試將Django的緩存中間件子類化爲在Header上不再有所不同。相反,它在匿名與登錄用戶之間有所不同。因爲我使用Auth後端,並且該處理程序在從緩存中獲取之前發生,它似乎工作。

class AnonymousUpdateCacheMiddleware(UpdateCacheMiddleware): 

    def process_response(self, request, response): 
     """ 
     Sets the cache, if needed. 
     We are overriding it in order to change the behavior of learn_cache_key(). 
     """ 

     if not self._should_update_cache(request, response): 
      # We don't need to update the cache, just return. 
      return response 
     if not response.status_code == 200: 
      return response 

     timeout = get_max_age(response) 
     if timeout == None: 
      timeout = self.cache_timeout 
     elif timeout == 0: 
      # max-age was set to 0, don't bother caching. 
      return response 
     patch_response_headers(response, timeout) 
     if timeout: 
      ######### HERE IS WHERE IT REALLY GOES DOWN ####### 
      cache_key = self.learn_cache_key(request, response, self.cache_timeout, self.key_prefix, cache=self.cache) 
      if hasattr(response, 'render') and callable(response.render): 
       response.add_post_render_callback(
        lambda r: self.cache.set(cache_key, r, timeout) 
       ) 
      else: 
       self.cache.set(cache_key, response, timeout) 
     return response 

    def learn_cache_key(self, request, response, timeout, key_prefix, cache=None): 
     """_generate_cache_header_key() creates a key for the given request path, adjusted for locales. 

      With this key, a new cache key is set via _generate_cache_key() for the HttpResponse 

      The subsequent anonymous request to this path hits the FetchFromCacheMiddleware in the 
      request capturing phase, which then looks up the headerlist value cached here on the initial response. 

      FetchFromMiddleWare calcuates a cache_key based on the values of the listed headers using _generate_cache_key 
      and then looks for the response stored under that key. If the headers are the same as those 
      set here, there will be a cache hit and the cached HTTPResponse is returned. 
     """ 

     key_prefix = key_prefix or settings.CACHE_MIDDLEWARE_KEY_PREFIX 
     cache_timeout = self.cache_timeout or settings.CACHE_MIDDLEWARE_SECONDS 
     cache = cache or get_cache(settings.CACHE_MIDDLEWARE_ALIAS) 

     cache_key = _generate_cache_header_key(key_prefix, request) 

     # Django normally varies caching by headers so that authed/anonymous users do not see same pages 
     # This makes Google Analytics cookies break caching; 
     # It also means that different anonymous session_ids break caching, so only first anon request works 
     # In this subclass, we are ignoring headers and instead varying on authed vs. anonymous users 
     # Alternatively, we could also strip cookies potentially for the same outcome 

     # if response.has_header('Vary'): 
     #  headerlist = ['HTTP_' + header.upper().replace('-', '_') 
     #     for header in cc_delim_re.split(response['Vary'])] 
     # else: 
     headerlist = [] 

     cache.set(cache_key, headerlist, cache_timeout) 
     return _generate_cache_key(request, request.method, headerlist, key_prefix) 

取出器,它負責從緩存中檢索的頁面,看起來像這樣

class AnonymousFetchFromCacheMiddleware(FetchFromCacheMiddleware): 

    def process_request(self, request): 
     """ 
     Checks whether the page is already cached and returns the cached 
     version if available. 
     """ 
     if request.user.is_authenticated(): 
      request._cache_update_cache = False 
      return None 
     else: 
      return super(SmarterFetchFromCacheMiddleware, self).process_request(request) 

有很多複製的UpdateCacheMiddleware,效果顯着。我找不出一個更好的鉤子來使這個更清潔。

這是否通常看起來像一個好方法?想到任何明顯的問題?

感謝, 本

回答

2

您可以解決此通過暫時除去不需要的變化領域從response['Vary']

from django.utils.cache import cc_delim_re 

class AnonymousUpdateCacheMiddleware(UpdateCacheMiddleware): 
    def process_response(self, request, response): 
     vary = None 
     if not request.user.is_authenticated() and response.has_header('Vary'): 
       vary = response['Vary'] 
       # only hide cookie here, add more as your usage 
       response['Vary'] = ', '.join(
        filter(lambda v: v != 'cookie', cc_delim_re.split(vary)) 
     response = super(AnonymousUpdateCacheMiddleware, self).process_response(request, response) 
     if vary is not None: 
      response['Vary'] = vary 
     return response 

此外,在設置中設置CACHE_MIDDLEWARE_ANONYMOUS_ONLY = True以防止緩存身份驗證的用戶。

+0

嗨okm,我的實現有效地做到這一點,對吧? – Ben

+0

@Ben沒有太大區別。但比較w /重複代碼,我更喜歡在這裏繼承。它更清晰,在你的項目中代碼少,當你升級Django版本 – okm

+0

感謝okm時,會導致更少的頭痛,所有好點 – Ben