Fixed #8693: Fixed formatting of the "null" argument for model fields
[django.git] / django / utils / cache.py
blob9c566ae099f962994d56d52dae75ff368746dce5
1 """
2 This module contains helper functions for controlling caching. It does so by
3 managing the "Vary" header of responses. It includes functions to patch the
4 header of response objects directly and decorators that change functions to do
5 that header-patching themselves.
7 For information on the Vary header, see:
9 http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44
11 Essentially, the "Vary" HTTP header defines which headers a cache should take
12 into account when building its cache key. Requests with the same path but
13 different header content for headers named in "Vary" need to get different
14 cache keys to prevent delivery of wrong content.
16 An example: i18n middleware would need to distinguish caches by the
17 "Accept-language" header.
18 """
20 import re
21 import time
22 try:
23 set
24 except NameError:
25 from sets import Set as set # Python 2.3 fallback
27 from django.conf import settings
28 from django.core.cache import cache
29 from django.utils.encoding import smart_str, iri_to_uri
30 from django.utils.http import http_date
31 from django.utils.hashcompat import md5_constructor
33 cc_delim_re = re.compile(r'\s*,\s*')
35 def patch_cache_control(response, **kwargs):
36 """
37 This function patches the Cache-Control header by adding all
38 keyword arguments to it. The transformation is as follows:
40 * All keyword parameter names are turned to lowercase, and underscores
41 are converted to hyphens.
42 * If the value of a parameter is True (exactly True, not just a
43 true value), only the parameter name is added to the header.
44 * All other parameters are added with their value, after applying
45 str() to it.
46 """
47 def dictitem(s):
48 t = s.split('=', 1)
49 if len(t) > 1:
50 return (t[0].lower(), t[1])
51 else:
52 return (t[0].lower(), True)
54 def dictvalue(t):
55 if t[1] is True:
56 return t[0]
57 else:
58 return t[0] + '=' + smart_str(t[1])
60 if response.has_header('Cache-Control'):
61 cc = cc_delim_re.split(response['Cache-Control'])
62 cc = dict([dictitem(el) for el in cc])
63 else:
64 cc = {}
66 # If there's already a max-age header but we're being asked to set a new
67 # max-age, use the minimum of the two ages. In practice this happens when
68 # a decorator and a piece of middleware both operate on a given view.
69 if 'max-age' in cc and 'max_age' in kwargs:
70 kwargs['max_age'] = min(cc['max-age'], kwargs['max_age'])
72 for (k, v) in kwargs.items():
73 cc[k.replace('_', '-')] = v
74 cc = ', '.join([dictvalue(el) for el in cc.items()])
75 response['Cache-Control'] = cc
77 def get_max_age(response):
78 """
79 Returns the max-age from the response Cache-Control header as an integer
80 (or ``None`` if it wasn't found or wasn't an integer.
81 """
82 if not response.has_header('Cache-Control'):
83 return
84 cc = dict([_to_tuple(el) for el in
85 cc_delim_re.split(response['Cache-Control'])])
86 if 'max-age' in cc:
87 try:
88 return int(cc['max-age'])
89 except (ValueError, TypeError):
90 pass
92 def patch_response_headers(response, cache_timeout=None):
93 """
94 Adds some useful headers to the given HttpResponse object:
95 ETag, Last-Modified, Expires and Cache-Control
97 Each header is only added if it isn't already set.
99 cache_timeout is in seconds. The CACHE_MIDDLEWARE_SECONDS setting is used
100 by default.
102 if cache_timeout is None:
103 cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
104 if cache_timeout < 0:
105 cache_timeout = 0 # Can't have max-age negative
106 if not response.has_header('ETag'):
107 response['ETag'] = '"%s"' % md5_constructor(response.content).hexdigest()
108 if not response.has_header('Last-Modified'):
109 response['Last-Modified'] = http_date()
110 if not response.has_header('Expires'):
111 response['Expires'] = http_date(time.time() + cache_timeout)
112 patch_cache_control(response, max_age=cache_timeout)
114 def add_never_cache_headers(response):
116 Adds headers to a response to indicate that a page should never be cached.
118 patch_response_headers(response, cache_timeout=-1)
120 def patch_vary_headers(response, newheaders):
122 Adds (or updates) the "Vary" header in the given HttpResponse object.
123 newheaders is a list of header names that should be in "Vary". Existing
124 headers in "Vary" aren't removed.
126 # Note that we need to keep the original order intact, because cache
127 # implementations may rely on the order of the Vary contents in, say,
128 # computing an MD5 hash.
129 if response.has_header('Vary'):
130 vary_headers = cc_delim_re.split(response['Vary'])
131 else:
132 vary_headers = []
133 # Use .lower() here so we treat headers as case-insensitive.
134 existing_headers = set([header.lower() for header in vary_headers])
135 additional_headers = [newheader for newheader in newheaders
136 if newheader.lower() not in existing_headers]
137 response['Vary'] = ', '.join(vary_headers + additional_headers)
139 def _generate_cache_key(request, headerlist, key_prefix):
140 """Returns a cache key from the headers given in the header list."""
141 ctx = md5_constructor()
142 for header in headerlist:
143 value = request.META.get(header, None)
144 if value is not None:
145 ctx.update(value)
146 return 'views.decorators.cache.cache_page.%s.%s.%s' % (
147 key_prefix, iri_to_uri(request.path), ctx.hexdigest())
149 def get_cache_key(request, key_prefix=None):
151 Returns a cache key based on the request path. It can be used in the
152 request phase because it pulls the list of headers to take into account
153 from the global path registry and uses those to build a cache key to check
154 against.
156 If there is no headerlist stored, the page needs to be rebuilt, so this
157 function returns None.
159 if key_prefix is None:
160 key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
161 cache_key = 'views.decorators.cache.cache_header.%s.%s' % (
162 key_prefix, iri_to_uri(request.path))
163 headerlist = cache.get(cache_key, None)
164 if headerlist is not None:
165 return _generate_cache_key(request, headerlist, key_prefix)
166 else:
167 return None
169 def learn_cache_key(request, response, cache_timeout=None, key_prefix=None):
171 Learns what headers to take into account for some request path from the
172 response object. It stores those headers in a global path registry so that
173 later access to that path will know what headers to take into account
174 without building the response object itself. The headers are named in the
175 Vary header of the response, but we want to prevent response generation.
177 The list of headers to use for cache key generation is stored in the same
178 cache as the pages themselves. If the cache ages some data out of the
179 cache, this just means that we have to build the response once to get at
180 the Vary header and so at the list of headers to use for the cache key.
182 if key_prefix is None:
183 key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
184 if cache_timeout is None:
185 cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
186 cache_key = 'views.decorators.cache.cache_header.%s.%s' % (
187 key_prefix, iri_to_uri(request.path))
188 if response.has_header('Vary'):
189 headerlist = ['HTTP_'+header.upper().replace('-', '_')
190 for header in cc_delim_re.split(response['Vary'])]
191 cache.set(cache_key, headerlist, cache_timeout)
192 return _generate_cache_key(request, headerlist, key_prefix)
193 else:
194 # if there is no Vary header, we still need a cache key
195 # for the request.path
196 cache.set(cache_key, [], cache_timeout)
197 return _generate_cache_key(request, [], key_prefix)
200 def _to_tuple(s):
201 t = s.split('=',1)
202 if len(t) == 2:
203 return t[0].lower(), t[1]
204 return t[0].lower(), True