diff --git a/django/middleware/cache.py b/django/middleware/cache.py index a88b4956b2..23a907f8be 100644 --- a/django/middleware/cache.py +++ b/django/middleware/cache.py @@ -1,14 +1,18 @@ from django.conf import settings from django.core.cache import cache -from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers +from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age class CacheMiddleware(object): """ Cache middleware. If this is enabled, each Django-powered page will be - cached for CACHE_MIDDLEWARE_SECONDS seconds. Cache is based on URLs. + cached (based on URLs). Only parameter-less GET or HEAD-requests with status code 200 are cached. + The number of seconds each page is stored for is set by the + "max-age" section of the response's "Cache-Control" header, falling back to + the CACHE_MIDDLEWARE_SECONDS setting if the section was not found. + If CACHE_MIDDLEWARE_ANONYMOUS_ONLY is set to True, only anonymous requests (i.e., those not made by a logged-in user) will be cached. This is a simple and effective way of avoiding the caching of the Django admin (and @@ -78,7 +82,16 @@ class CacheMiddleware(object): return response if not response.status_code == 200: return response - patch_response_headers(response, self.cache_timeout) - cache_key = learn_cache_key(request, response, self.cache_timeout, self.key_prefix) - cache.set(cache_key, response, self.cache_timeout) + # Try to get the timeout from the "max-age" section of the "Cache- + # Control" header before reverting to using the default cache_timeout + # length. + 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) + cache_key = learn_cache_key(request, response, timeout, self.key_prefix) + cache.set(cache_key, response, timeout) return response diff --git a/django/utils/cache.py b/django/utils/cache.py index 5654bed7aa..4fcf493944 100644 --- a/django/utils/cache.py +++ b/django/utils/cache.py @@ -74,6 +74,21 @@ def patch_cache_control(response, **kwargs): cc = ', '.join([dictvalue(el) for el in cc.items()]) response['Cache-Control'] = cc +def get_max_age(response): + """ + Returns the max-age from the response Cache-Control header as an integer + (or ``None`` if it wasn't found or wasn't an integer. + """ + if not response.has_header('Cache-Control'): + return + cc = dict([_to_tuple(el) for el in + cc_delim_re.split(response['Cache-Control'])]) + if 'max-age' in cc: + try: + return int(cc['max-age']) + except (ValueError, TypeError): + pass + def patch_response_headers(response, cache_timeout=None): """ Adds some useful headers to the given HttpResponse object: @@ -180,3 +195,10 @@ def learn_cache_key(request, response, cache_timeout=None, key_prefix=None): # for the request.path cache.set(cache_key, [], cache_timeout) return _generate_cache_key(request, [], key_prefix) + + +def _to_tuple(s): + t = s.split('=',1) + if len(t) == 2: + return t[0].lower(), t[1] + return t[0].lower(), True diff --git a/docs/cache.txt b/docs/cache.txt index 4f177b8c07..6dfe2bf5ee 100644 --- a/docs/cache.txt +++ b/docs/cache.txt @@ -263,6 +263,18 @@ See the `middleware documentation`_ for more on middleware. .. _`middleware documentation`: ../middleware/ +**New in Django development version** + +If a view sets its own cache expiry time (i.e. it has a ``max-age`` section in +its ``Cache-Control`` header) then the page will be cached until the expiry +time, rather than ``CACHE_MIDDLEWARE_SECONDS``. Using the decorators in +``django.views.decorators.cache`` you can easily set a view's expiry time +(using the ``cache_control`` decorator) or disable caching for a view (using +the ``never_cache`` decorator). See the `using other headers`__ section for +more on these decorators. + +__ `Controlling cache: Using other headers`_ + The per-view cache ================== @@ -566,7 +578,7 @@ the value of the ``CACHE_MIDDLEWARE_SETTINGS`` setting. If you use a custom precedence, and the header values will be merged correctly.) If you want to use headers to disable caching altogether, -``django.views.decorators.never_cache`` is a view decorator that adds +``django.views.decorators.cache.never_cache`` is a view decorator that adds headers to ensure the response won't be cached by browsers or other caches. Example:: from django.views.decorators.cache import never_cache