Django

Code

Changeset 8260

Show
Ignore:
Timestamp:
08/09/08 10:07:45 (4 months ago)
Author:
jacob
Message:

Split CacheMiddleware? up into two parts -- an update-cache and a fetch-from-cache middleware. This lets you run each half of the cache middleware at the correct time to avoid bad interactions between the cache middleware and other middleware that must modify the cache key (like the locale middleware).

CacheMiddleware? itself is still around for backwards-compatibility and as a hook point for the cache decorator, but the documentation has been updated to point people towards the two-part caching middleware.

Refs #730.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/middleware/cache.py

    r6736 r8260  
     1""" 
     2Cache middleware. If enabled, each Django-powered page will be cached based on 
     3URL. The cannonical 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. However, 
     19if any other peice of middleware needs to affect the cache key, you'll need to 
     20use the two-part UpdateCacheMiddleware and FetchFromCacheMiddleware. This'll 
     21most often happen when you're using Django's LocaleMiddleware. 
     22 
     23More details about how the caching works: 
     24 
     25* Only parameter-less GET or HEAD-requests with status code 200 are cached. 
     26 
     27* The number of seconds each page is stored for is set by the "max-age" section 
     28  of the response's "Cache-Control" header, falling back to the 
     29  CACHE_MIDDLEWARE_SECONDS setting if the section was not found. 
     30 
     31* If CACHE_MIDDLEWARE_ANONYMOUS_ONLY is set to True, only anonymous requests 
     32  (i.e., those not made by a logged-in user) will be cached. This is a simple 
     33  and effective way of avoiding the caching of the Django admin (and any other 
     34  user-specific content). 
     35 
     36* This middleware expects that a HEAD request is answered with a response 
     37  exactly like the corresponding GET request. 
     38 
     39* When a hit occurs, a shallow copy of the original response object is returned 
     40  from process_request. 
     41 
     42* Pages will be cached based on the contents of the request headers listed in 
     43  the response's "Vary" header. 
     44 
     45* This middleware also sets ETag, Last-Modified, Expires and Cache-Control 
     46  headers on the response object. 
     47 
     48""" 
     49 
    150from django.conf import settings 
    251from django.core.cache import cache 
    352from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age 
    453 
    5 class CacheMiddleware(object): 
     54class UpdateCacheMiddleware(object): 
    655    """ 
    7     Cache middleware. If this is enabled, each Django-powered page will be 
    8     cached (based on URLs). 
     56    Response-phase cache middleware that updates the cache if the response is 
     57    cacheable. 
     58     
     59    Must be used as part of the two-part update/fetch cache middleware. 
     60    UpdateCacheMiddleware must be the first piece of middleware in 
     61    MIDDLEWARE_CLASSES so that it'll get called last during the response phase. 
     62    """ 
     63    def __init__(self): 
     64        self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS 
     65        self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX 
     66        self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False) 
    967 
    10     Only parameter-less GET or HEAD-requests with status code 200 are cached. 
     68    def process_response(self, request, response): 
     69        """Sets the cache, if needed.""" 
     70        if not hasattr(request, '_cache_update_cache') or not request._cache_update_cache: 
     71            # We don't need to update the cache, just return. 
     72            return response 
     73        if request.method != 'GET': 
     74            # This is a stronger requirement than above. It is needed 
     75            # because of interactions between this middleware and the 
     76            # HTTPMiddleware, which throws the body of a HEAD-request 
     77            # away before this middleware gets a chance to cache it. 
     78            return response 
     79        if not response.status_code == 200: 
     80            return response 
     81        # Try to get the timeout from the "max-age" section of the "Cache- 
     82        # Control" header before reverting to using the default cache_timeout 
     83        # length. 
     84        timeout = get_max_age(response) 
     85        if timeout == None: 
     86            timeout = self.cache_timeout 
     87        elif timeout == 0: 
     88            # max-age was set to 0, don't bother caching. 
     89            return response 
     90        patch_response_headers(response, timeout) 
     91        cache_key = learn_cache_key(request, response, timeout, self.key_prefix) 
     92        cache.set(cache_key, response, timeout) 
     93        return response 
    1194 
    12     The number of seconds each page is stored for is set by the 
    13     "max-age" section of the response's "Cache-Control" header, falling back to 
    14     the CACHE_MIDDLEWARE_SECONDS setting if the section was not found. 
    15  
    16     If CACHE_MIDDLEWARE_ANONYMOUS_ONLY is set to True, only anonymous requests 
    17     (i.e., those not made by a logged-in user) will be cached. This is a 
    18     simple and effective way of avoiding the caching of the Django admin (and 
    19     any other user-specific content). 
    20  
    21     This middleware expects that a HEAD request is answered with a response 
    22     exactly like the corresponding GET request. 
    23  
    24     When a hit occurs, a shallow copy of the original response object is 
    25     returned from process_request. 
    26  
    27     Pages will be cached based on the contents of the request headers 
    28     listed in the response's "Vary" header. This means that pages shouldn't 
    29     change their "Vary" header. 
    30  
    31     This middleware also sets ETag, Last-Modified, Expires and Cache-Control 
    32     headers on the response object. 
     95class FetchFromCacheMiddleware(object): 
    3396    """ 
    34     def __init__(self, cache_timeout=None, key_prefix=None, cache_anonymous_only=None): 
    35         self.cache_timeout = cache_timeout 
    36         if cache_timeout is None: 
    37             self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS 
    38         self.key_prefix = key_prefix 
    39         if key_prefix is None: 
    40             self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX 
    41         if cache_anonymous_only is None: 
    42             self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False) 
    43         else: 
    44             self.cache_anonymous_only = cache_anonymous_only 
    45  
     97    Request-phase cache middleware that fetches a page from the cache. 
     98     
     99    Must be used as part of the two-part update/fetch cache middleware. 
     100    FetchFromCacheMiddleware must be the last piece of middleware in 
     101    MIDDLEWARE_CLASSES so that it'll get called last during the request phase. 
     102    """ 
     103    def __init__(self): 
     104        self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS 
     105        self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX 
     106        self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False) 
     107     
    46108    def process_request(self, request): 
    47         "Checks whether the page is already cached and returns the cached version if available." 
     109        """ 
     110        Checks whether the page is already cached and returns the cached 
     111        version if available. 
     112        """ 
    48113        if self.cache_anonymous_only: 
    49114            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." 
     
    70135        return response 
    71136 
    72     def process_response(self, request, response): 
    73         "Sets the cache, if needed." 
    74         if not hasattr(request, '_cache_update_cache') or not request._cache_update_cache: 
    75             # We don't need to update the cache, just return. 
    76             return response 
    77         if request.method != 'GET': 
    78             # This is a stronger requirement than above. It is needed 
    79             # because of interactions between this middleware and the 
    80             # HTTPMiddleware, which throws the body of a HEAD-request 
    81             # away before this middleware gets a chance to cache it. 
    82             return response 
    83         if not response.status_code == 200: 
    84             return response 
    85         # Try to get the timeout from the "max-age" section of the "Cache- 
    86         # Control" header before reverting to using the default cache_timeout 
    87         # length. 
    88         timeout = get_max_age(response) 
    89         if timeout == None: 
    90             timeout = self.cache_timeout 
    91         elif timeout == 0: 
    92             # max-age was set to 0, don't bother caching. 
    93             return response 
    94         patch_response_headers(response, timeout) 
    95         cache_key = learn_cache_key(request, response, timeout, self.key_prefix) 
    96         cache.set(cache_key, response, timeout) 
    97         return response 
     137class CacheMiddleware(UpdateCacheMiddleware, FetchFromCacheMiddleware): 
     138    """ 
     139    Cache middleware that provides basic behavior for many simple sites. 
     140     
     141    Also used as the hook point for the cache decorator, which is generated 
     142    using the decorator-from-middleware utility. 
     143    """ 
     144    def __init__(self, cache_timeout=None, key_prefix=None, cache_anonymous_only=None): 
     145        self.cache_timeout = cache_timeout 
     146        if cache_timeout is None: 
     147            self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS 
     148        self.key_prefix = key_prefix 
     149        if key_prefix is None: 
     150            self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX 
     151        if cache_anonymous_only is None: 
     152            self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False) 
     153        else: 
     154            self.cache_anonymous_only = cache_anonymous_only 
     155 
  • django/trunk/docs/cache.txt

    r8191 r8260  
    232232================== 
    233233 
     234**New in Django development version** (previous versions of Django only provided a single ``CacheMiddleware`` instead of the two pieces described below). 
     235 
    234236Once the cache is set up, the simplest way to use caching is to cache your 
    235 entire site. Just add ``'django.middleware.cache.CacheMiddleware'`` to your 
     237entire site. You'll need to add 
     238``'django.middleware.cache.UpdateCacheMiddleware'`` and 
     239``'django.middleware.cache.FetchFromCacheMiddleware' to your 
    236240``MIDDLEWARE_CLASSES`` setting, as in this example:: 
    237241 
    238242    MIDDLEWARE_CLASSES = ( 
    239         'django.middleware.cache.CacheMiddleware', 
     243        'django.middleware.cache.UpdateCacheMiddleware', 
    240244        'django.middleware.common.CommonMiddleware', 
     245        'django.middleware.cache.FetchFromCacheMiddleware', 
    241246    ) 
    242247 
    243 (The order of ``MIDDLEWARE_CLASSES`` matters. See `Order of MIDDLEWARE_CLASSES`_ 
    244 below.) 
     248.. note:: 
     249 
     250    No, that's not a typo: the "update" middleware must be first in the list, 
     251    and the "fetch" middleware must be last. The details are a bit obscure, but 
     252    see `Order of MIDDLEWARE_CLASSES`_ below if you'd like the full story. 
    245253 
    246254Then, add the following required settings to your Django settings file: 
     
    259267user-specific pages (include Django's admin interface). Note that if you use 
    260268``CACHE_MIDDLEWARE_ANONYMOUS_ONLY``, you should make sure you've activated 
    261 ``AuthenticationMiddleware`` and that ``AuthenticationMiddleware`` appears 
    262 before ``CacheMiddleware`` in your ``MIDDLEWARE_CLASSES``. 
    263  
    264 Additionally, ``CacheMiddleware`` automatically sets a few headers in each 
     269``AuthenticationMiddleware``. 
     270 
     271Additionally, the cache middleware automatically sets a few headers in each 
    265272``HttpResponse``: 
    266273 
     
    628635=========================== 
    629636 
    630 If you use ``CacheMiddleware``, it's important to put it in the right place 
    631 within the ``MIDDLEWARE_CLASSES`` setting, because the cache middleware needs 
    632 to know which headers by which to vary the cache storage. Middleware always 
    633 adds something to the ``Vary`` response header when it can. 
    634  
    635 Put the ``CacheMiddleware`` *before* any other middleware that might add 
    636 something to the ``Vary`` header (response middleware is applied in reverse 
    637 order). The following middleware modules do so: 
     637If you use caching middlewaare, it's important to put each half in the right 
     638place within the ``MIDDLEWARE_CLASSES`` setting. That's because the cache 
     639middleware needs to know which headers by which to vary the cache storage. 
     640Middleware always adds something to the ``Vary`` response header when it can. 
     641 
     642``UpdateCacheMiddleware`` runs during the response phase, where middleware is 
     643run in reverse order, so an item at the top of the list runs *last* during the 
     644response phase. Thus, you need to make sure that ``UpdateCacheMiddleware`` 
     645appears *before* any other middleware that might add something to the ``Vary`` 
     646header. The following middleware modules do so: 
    638647 
    639648    * ``SessionMiddleware`` adds ``Cookie`` 
    640649    * ``GZipMiddleware`` adds ``Accept-Encoding`` 
    641650    * ``LocaleMiddleware`` adds ``Accept-Language`` 
    642  
     651     
     652``FetchFromCacheMiddleware``, on the other hand, runs during the request phase, 
     653where middleware is applied first-to-last, so an item at the top of the list 
     654runs *first* during the request phase. The ``FetchFromCacheMiddleware`` also 
     655needs to run after other middleware updates the ``Vary`` header, so 
     656``FetchFromCacheMiddleware`` must be *after* any item that does so. 
     657