PageRenderTime 39ms CodeModel.GetById 21ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

/django/middleware/cache.py

https://code.google.com/p/mango-py/
Python | 205 lines | 151 code | 12 blank | 42 comment | 21 complexity | 080e87991c5bb9630aa2a10055220a19 MD5 | raw file
  1"""
  2Cache middleware. If enabled, each Django-powered page will be cached based on
  3URL. The canonical way to enable cache middleware is to set
  4``UpdateCacheMiddleware`` as your first piece of middleware, and
  5``FetchFromCacheMiddleware`` as the last::
  6
  7    MIDDLEWARE_CLASSES = [
  8        'django.middleware.cache.UpdateCacheMiddleware',
  9        ...
 10        'django.middleware.cache.FetchFromCacheMiddleware'
 11    ]
 12
 13This is counter-intuitive, but correct: ``UpdateCacheMiddleware`` needs to run
 14last during the response phase, which processes middleware bottom-up;
 15``FetchFromCacheMiddleware`` needs to run last during the request phase, which
 16processes middleware top-down.
 17
 18The single-class ``CacheMiddleware`` can be used for some simple sites.
 19However, if any other piece of middleware needs to affect the cache key, you'll
 20need to use the two-part ``UpdateCacheMiddleware`` and
 21``FetchFromCacheMiddleware``. This'll most often happen when you're using
 22Django's ``LocaleMiddleware``.
 23
 24More details about how the caching works:
 25
 26* Only GET or HEAD-requests with status code 200 are cached.
 27
 28* The number of seconds each page is stored for is set by the "max-age" section
 29  of the response's "Cache-Control" header, falling back to the
 30  CACHE_MIDDLEWARE_SECONDS setting if the section was not found.
 31
 32* If CACHE_MIDDLEWARE_ANONYMOUS_ONLY is set to True, only anonymous requests
 33  (i.e., those not made by a logged-in user) will be cached. This is a simple
 34  and effective way of avoiding the caching of the Django admin (and any other
 35  user-specific content).
 36
 37* This middleware expects that a HEAD request is answered with the same response
 38  headers exactly like the corresponding GET request.
 39
 40* When a hit occurs, a shallow copy of the original response object is returned
 41  from process_request.
 42
 43* Pages will be cached based on the contents of the request headers listed in
 44  the response's "Vary" header.
 45
 46* This middleware also sets ETag, Last-Modified, Expires and Cache-Control
 47  headers on the response object.
 48
 49"""
 50
 51from django.conf import settings
 52from django.core.cache import get_cache, DEFAULT_CACHE_ALIAS
 53from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age
 54
 55
 56class UpdateCacheMiddleware(object):
 57    """
 58    Response-phase cache middleware that updates the cache if the response is
 59    cacheable.
 60
 61    Must be used as part of the two-part update/fetch cache middleware.
 62    UpdateCacheMiddleware must be the first piece of middleware in
 63    MIDDLEWARE_CLASSES so that it'll get called last during the response phase.
 64    """
 65    def __init__(self):
 66        self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
 67        self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
 68        self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
 69        self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS
 70        self.cache = get_cache(self.cache_alias)
 71
 72    def _session_accessed(self, request):
 73        try:
 74            return request.session.accessed
 75        except AttributeError:
 76            return False
 77
 78    def _should_update_cache(self, request, response):
 79        if not hasattr(request, '_cache_update_cache') or not request._cache_update_cache:
 80            return False
 81        # If the session has not been accessed otherwise, we don't want to
 82        # cause it to be accessed here. If it hasn't been accessed, then the
 83        # user's logged-in status has not affected the response anyway.
 84        if self.cache_anonymous_only and self._session_accessed(request):
 85            assert hasattr(request, 'user'), "The Django cache middleware with CACHE_MIDDLEWARE_ANONYMOUS_ONLY=True requires authentication middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.auth.middleware.AuthenticationMiddleware' before the CacheMiddleware."
 86            if request.user.is_authenticated():
 87                # Don't cache user-variable requests from authenticated users.
 88                return False
 89        return True
 90
 91    def process_response(self, request, response):
 92        """Sets the cache, if needed."""
 93        if not self._should_update_cache(request, response):
 94            # We don't need to update the cache, just return.
 95            return response
 96        if not response.status_code == 200:
 97            return response
 98        # Try to get the timeout from the "max-age" section of the "Cache-
 99        # Control" header before reverting to using the default cache_timeout
100        # length.
101        timeout = get_max_age(response)
102        if timeout == None:
103            timeout = self.cache_timeout
104        elif timeout == 0:
105            # max-age was set to 0, don't bother caching.
106            return response
107        patch_response_headers(response, timeout)
108        if timeout:
109            cache_key = learn_cache_key(request, response, timeout, self.key_prefix, cache=self.cache)
110            if hasattr(response, 'render') and callable(response.render):
111                response.add_post_render_callback(
112                    lambda r: self.cache.set(cache_key, r, timeout)
113                )
114            else:
115                self.cache.set(cache_key, response, timeout)
116        return response
117
118class FetchFromCacheMiddleware(object):
119    """
120    Request-phase cache middleware that fetches a page from the cache.
121
122    Must be used as part of the two-part update/fetch cache middleware.
123    FetchFromCacheMiddleware must be the last piece of middleware in
124    MIDDLEWARE_CLASSES so that it'll get called last during the request phase.
125    """
126    def __init__(self):
127        self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
128        self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
129        self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
130        self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS
131        self.cache = get_cache(self.cache_alias)
132
133    def process_request(self, request):
134        """
135        Checks whether the page is already cached and returns the cached
136        version if available.
137        """
138        if not request.method in ('GET', 'HEAD'):
139            request._cache_update_cache = False
140            return None # Don't bother checking the cache.
141
142        # try and get the cached GET response
143        cache_key = get_cache_key(request, self.key_prefix, 'GET', cache=self.cache)
144        if cache_key is None:
145            request._cache_update_cache = True
146            return None # No cache information available, need to rebuild.
147        response = self.cache.get(cache_key, None)
148        # if it wasn't found and we are looking for a HEAD, try looking just for that
149        if response is None and request.method == 'HEAD':
150            cache_key = get_cache_key(request, self.key_prefix, 'HEAD', cache=self.cache)
151            response = self.cache.get(cache_key, None)
152
153        if response is None:
154            request._cache_update_cache = True
155            return None # No cache information available, need to rebuild.
156
157        # hit, return cached response
158        request._cache_update_cache = False
159        return response
160
161class CacheMiddleware(UpdateCacheMiddleware, FetchFromCacheMiddleware):
162    """
163    Cache middleware that provides basic behavior for many simple sites.
164
165    Also used as the hook point for the cache decorator, which is generated
166    using the decorator-from-middleware utility.
167    """
168    def __init__(self, cache_timeout=None, cache_anonymous_only=None, **kwargs):
169        # We need to differentiate between "provided, but using default value",
170        # and "not provided". If the value is provided using a default, then
171        # we fall back to system defaults. If it is not provided at all,
172        # we need to use middleware defaults.
173
174        cache_kwargs = {}
175
176        try:
177            self.key_prefix = kwargs['key_prefix']
178            if self.key_prefix is not None:
179                cache_kwargs['KEY_PREFIX'] = self.key_prefix
180            else:
181                self.key_prefix = ''
182        except KeyError:
183            self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
184            cache_kwargs['KEY_PREFIX'] = self.key_prefix
185
186        try:
187            self.cache_alias = kwargs['cache_alias']
188            if self.cache_alias is None:
189                self.cache_alias = DEFAULT_CACHE_ALIAS
190            if cache_timeout is not None:
191                cache_kwargs['TIMEOUT'] = cache_timeout
192        except KeyError:
193            self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS
194            if cache_timeout is None:
195                cache_kwargs['TIMEOUT'] = settings.CACHE_MIDDLEWARE_SECONDS
196            else:
197                cache_kwargs['TIMEOUT'] = cache_timeout
198
199        if cache_anonymous_only is None:
200            self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
201        else:
202            self.cache_anonymous_only = cache_anonymous_only
203
204        self.cache = get_cache(self.cache_alias, **cache_kwargs)
205        self.cache_timeout = self.cache.default_timeout