2012-06-07 6 views
7

私はUpdateCacheMiddlewareFetchFromCacheMiddlewareミドルウェアを使用して、さまざまなレベルの成功へのサイト全体の匿名キャッシングを可能にしました。Django UpdateCacheMiddlewareとFetchFromCacheMiddlewareをサブクラス化するテクニック

最も大きな問題は、ミドルウェアが匿名ユーザーの最初のリクエストをキャッシュすることだけです。最初のレスポンスでsession_idクッキーが設定されているため、その匿名ユーザーによる後続のリクエストは、ヘッダーで変化するビュー・レベル・キャッシュの結果としてキャッシュにヒットしません。

私のウェブページは、匿名ユーザーの間では意味がありません。違いがある限り、私はそれをAjax経由で処理できます。その結果、私はDjangoのキャッシングミドルウェアのサブクラス化をやめ、Headerを変更しないことにしました。代わりに、匿名ユーザーとLoggedInユーザーで異なります。 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) 

キャッシュからページを取得するための責任があるフェッチャは、コピーの多くは、明らかに、UpdateCacheMiddlewareのためにありました。この

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) 

のように見えます。私はこのクリーナーを作る良いフックを理解することができませんでした。

これは一般的には良いアプローチのようですか?心に浮かぶ明らかな問題?

おかげで、 ベン

答えて

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

こんにちは、私の実装は効果的にそうですよね? – Ben

+0

@Ben大きな違いはありません。しかし、コードを複製することと比較して、私はここで継承を優先します。あなたがプロジェクトでコードが少なくて済み、Djangoバージョン – okm

+0

をアップグレードすると頭痛が減りますokm、すべての良い点 – Ben

関連する問題