web/lib/django/middleware/cache.py
changeset 38 77b6da96e6f1
parent 0 0d40e90630ef
equal deleted inserted replaced
37:8d941af65caf 38:77b6da96e6f1
       
     1 """
       
     2 Cache middleware. If enabled, each Django-powered page will be cached based on
       
     3 URL. 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 
       
    13 This is counter-intuitive, but correct: ``UpdateCacheMiddleware`` needs to run
       
    14 last during the response phase, which processes middleware bottom-up;
       
    15 ``FetchFromCacheMiddleware`` needs to run last during the request phase, which
       
    16 processes middleware top-down.
       
    17 
       
    18 The single-class ``CacheMiddleware`` can be used for some simple sites.
       
    19 However, if any other piece of middleware needs to affect the cache key, you'll
       
    20 need to use the two-part ``UpdateCacheMiddleware`` and
       
    21 ``FetchFromCacheMiddleware``. This'll most often happen when you're using
       
    22 Django's ``LocaleMiddleware``.
       
    23 
       
    24 More details about how the caching works:
       
    25 
       
    26 * Only parameter-less 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 a response
       
    38   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 
       
    51 from django.conf import settings
       
    52 from django.core.cache import cache
       
    53 from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age
       
    54 
       
    55 class UpdateCacheMiddleware(object):
       
    56     """
       
    57     Response-phase cache middleware that updates the cache if the response is
       
    58     cacheable.
       
    59 
       
    60     Must be used as part of the two-part update/fetch cache middleware.
       
    61     UpdateCacheMiddleware must be the first piece of middleware in
       
    62     MIDDLEWARE_CLASSES so that it'll get called last during the response phase.
       
    63     """
       
    64     def __init__(self):
       
    65         self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
       
    66         self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
       
    67         self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
       
    68 
       
    69     def process_response(self, request, response):
       
    70         """Sets the cache, if needed."""
       
    71         if not hasattr(request, '_cache_update_cache') or not request._cache_update_cache:
       
    72             # We don't need to update the cache, just return.
       
    73             return response
       
    74         if request.method != 'GET':
       
    75             # This is a stronger requirement than above. It is needed
       
    76             # because of interactions between this middleware and the
       
    77             # HTTPMiddleware, which throws the body of a HEAD-request
       
    78             # away before this middleware gets a chance to cache it.
       
    79             return response
       
    80         if not response.status_code == 200:
       
    81             return response
       
    82         # Try to get the timeout from the "max-age" section of the "Cache-
       
    83         # Control" header before reverting to using the default cache_timeout
       
    84         # length.
       
    85         timeout = get_max_age(response)
       
    86         if timeout == None:
       
    87             timeout = self.cache_timeout
       
    88         elif timeout == 0:
       
    89             # max-age was set to 0, don't bother caching.
       
    90             return response
       
    91         patch_response_headers(response, timeout)
       
    92         if timeout:
       
    93             cache_key = learn_cache_key(request, response, timeout, self.key_prefix)
       
    94             cache.set(cache_key, response, timeout)
       
    95         return response
       
    96 
       
    97 class FetchFromCacheMiddleware(object):
       
    98     """
       
    99     Request-phase cache middleware that fetches a page from the cache.
       
   100 
       
   101     Must be used as part of the two-part update/fetch cache middleware.
       
   102     FetchFromCacheMiddleware must be the last piece of middleware in
       
   103     MIDDLEWARE_CLASSES so that it'll get called last during the request phase.
       
   104     """
       
   105     def __init__(self):
       
   106         self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
       
   107         self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
       
   108         self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
       
   109 
       
   110     def process_request(self, request):
       
   111         """
       
   112         Checks whether the page is already cached and returns the cached
       
   113         version if available.
       
   114         """
       
   115         if self.cache_anonymous_only:
       
   116             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."
       
   117 
       
   118         if not request.method in ('GET', 'HEAD') or request.GET:
       
   119             request._cache_update_cache = False
       
   120             return None # Don't bother checking the cache.
       
   121 
       
   122         if self.cache_anonymous_only and request.user.is_authenticated():
       
   123             request._cache_update_cache = False
       
   124             return None # Don't cache requests from authenticated users.
       
   125 
       
   126         cache_key = get_cache_key(request, self.key_prefix)
       
   127         if cache_key is None:
       
   128             request._cache_update_cache = True
       
   129             return None # No cache information available, need to rebuild.
       
   130 
       
   131         response = cache.get(cache_key, None)
       
   132         if response is None:
       
   133             request._cache_update_cache = True
       
   134             return None # No cache information available, need to rebuild.
       
   135 
       
   136         request._cache_update_cache = False
       
   137         return response
       
   138 
       
   139 class CacheMiddleware(UpdateCacheMiddleware, FetchFromCacheMiddleware):
       
   140     """
       
   141     Cache middleware that provides basic behavior for many simple sites.
       
   142 
       
   143     Also used as the hook point for the cache decorator, which is generated
       
   144     using the decorator-from-middleware utility.
       
   145     """
       
   146     def __init__(self, cache_timeout=None, key_prefix=None, cache_anonymous_only=None):
       
   147         self.cache_timeout = cache_timeout
       
   148         if cache_timeout is None:
       
   149             self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
       
   150         self.key_prefix = key_prefix
       
   151         if key_prefix is None:
       
   152             self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
       
   153         if cache_anonymous_only is None:
       
   154             self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
       
   155         else:
       
   156             self.cache_anonymous_only = cache_anonymous_only