Code

Ticket #612: cache-control.diff

File cache-control.diff, 5.2 KB (added by hugo, 9 years ago)

Cache-Control managing decorator and function

Line 
1Index: django/utils/cache.py
2===================================================================
3--- django/utils/cache.py       (revision 835)
4+++ django/utils/cache.py       (working copy)
5@@ -21,6 +21,45 @@
6 from django.conf import settings
7 from django.core.cache import cache
8 
9+cc_delim_re = re.compile(r'\s*,\s*')
10+def patch_cache_control(response, **kwargs):
11+    """
12+    This function patches the Cache-Control header by adding all
13+    keyword arguments to it. The transformation is as follows:
14+
15+    - all keyword parameter names are turned to lowercase and
16+      all _ will be translated to -
17+    - if the value of a parameter is True (exatly True, not just a
18+      true value), only the parameter name is added to the header
19+    - all other parameters are added with their value, after applying
20+      str to it.
21+    """
22+
23+    def dictitem(s):
24+        t = s.split('=',1)
25+        if len(t) > 1:
26+            return (t[0].lower().replace('-', '_'), t[1])
27+        else:
28+            return (t[0].lower().replace('-', '_'), True)
29+
30+    def dictvalue(t):
31+        if t[1] == True:
32+            return t[0]
33+        else:
34+            return t[0] + '=' + str(t[1])
35+
36+    if response.has_header('Cache-Control'):
37+        print response['Cache-Control']
38+        cc = cc_delim_re.split(response['Cache-Control'])
39+        print cc
40+        cc = dict([dictitem(el) for el in cc])
41+    else:
42+        cc = {}
43+    for (k,v) in kwargs.items():
44+        cc[k.replace('_', '-')] = v
45+    cc = ', '.join([dictvalue(el) for el in cc.items()])
46+    response['Cache-Control'] = cc
47+
48 vary_delim_re = re.compile(r',\s*')
49 
50 def patch_response_headers(response, cache_timeout=None):
51@@ -43,8 +82,7 @@
52         response['Last-Modified'] = now.strftime('%a, %d %b %Y %H:%M:%S GMT')
53     if not response.has_header('Expires'):
54         response['Expires'] = expires.strftime('%a, %d %b %Y %H:%M:%S GMT')
55-    if not response.has_header('Cache-Control'):
56-        response['Cache-Control'] = 'max-age=%d' % cache_timeout
57+    patch_cache_control(response, max_age=cache_timeout)
58 
59 def patch_vary_headers(response, newheaders):
60     """
61Index: django/views/decorators/cache.py
62===================================================================
63--- django/views/decorators/cache.py    (revision 835)
64+++ django/views/decorators/cache.py    (working copy)
65@@ -10,8 +10,24 @@
66 Additionally, all headers from the response's Vary header will be taken into
67 account on caching -- just like the middleware does.
68 """
69+import re
70 
71 from django.utils.decorators import decorator_from_middleware
72+from django.utils.cache import patch_cache_control
73 from django.middleware.cache import CacheMiddleware
74 
75 cache_page = decorator_from_middleware(CacheMiddleware)
76+
77+def cache_control(**kwargs):
78+
79+    def _cache_controller(viewfunc):
80+
81+        def _cache_controlled(request, *args, **kw):
82+            response = viewfunc(request, *args, **kw)
83+            patch_cache_control(response, **kwargs)
84+            return response
85+
86+        return _cache_controlled
87+
88+    return _cache_controller
89+
90Index: docs/cache.txt
91===================================================================
92--- docs/cache.txt      (revision 835)
93+++ docs/cache.txt      (working copy)
94@@ -270,6 +270,40 @@
95 
96 .. _`HTTP Vary headers`: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44
97 
98+Controlling cache: Using Vary headers
99+=====================================
100+
101+Another problem with caching is the privacy of data, and the question where data can
102+be stored in a cascade of caches. A user usually faces two kinds of caches: his own
103+browser cache (a private cache) and his providers cache (a public cache). A public cache
104+is used by multiple users and controlled by someone else. This poses problems with private
105+(in the sense of sensitive) data - you don't want your social security number or your
106+banking account numbers stored in some public cache. So web applications need a way
107+to tell the caches what data is private and what is public.
108+
109+Other aspects are the definition how long a page should be cached at max, or wether the
110+cache should allways check for newer versions and only deliver the cache content when
111+there were no changes (some caches might deliver cached content even if the server page
112+changed - just because the cache copy isn't yet expired).
113+
114+So there are a multitude of options you can control for your pages. This is where the
115+Cache-Control header (more infos in `HTTP Cache-Control headers`_) comes in. The usage
116+is quite simple::
117+
118+    @cache_control(private=True, must_revalidate=True, max_age=3600)
119+    def my_view(request):
120+        ...
121+
122+This would define the view as private, to be revalidated on every access and cache
123+copies will only be stored for 3600 seconds at max.
124+
125+The caching middleware already set's this header up with a max-age of the CACHE_MIDDLEWARE_SETTINGS
126+setting. And the cache_page decorator does the same. The cache_control decorator correctly merges
127+different values into one big header, though. But you should take into account that middlewares
128+might overwrite some of your headers or set their own defaults if you don't give that header yourself.
129+
130+.. _`HTTP Cache-Control headers`: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
131+
132 Other optimizations
133 ===================
134