Code

Ticket #4992: cache_by_request_full_path_v2.diff

File cache_by_request_full_path_v2.diff, 7.0 KB (added by guettli, 4 years ago)

Updated patch of PeterKz to apply cleanly to trunk. Added tests

Line 
1Index: docs/topics/cache.txt
2===================================================================
3--- docs/topics/cache.txt       (Revision 13963)
4+++ docs/topics/cache.txt       (Arbeitskopie)
5@@ -731,8 +731,8 @@
6 said to "vary on language."
7 
8 By default, Django's cache system creates its cache keys using the requested
9-path (e.g., ``"/stories/2005/jun/23/bank_robbed/"``). This means every request
10-to that URL will use the same cached version, regardless of user-agent
11+path and query -- e.g., ``"/stories/2005/?order_by=author"``. This means every
12+request to that URL will use the same cached version, regardless of user-agent
13 differences such as cookies or language preferences. However, if this page
14 produces different content based on some difference in request headers -- such
15 as a cookie, or a language, or a user-agent -- you'll need to use the ``Vary``
16Index: django/utils/cache.py
17===================================================================
18--- django/utils/cache.py       (Revision 13963)
19+++ django/utils/cache.py       (Arbeitskopie)
20@@ -150,21 +150,21 @@
21         value = request.META.get(header, None)
22         if value is not None:
23             ctx.update(value)
24-    path = md5_constructor(iri_to_uri(request.path))
25+    path = md5_constructor(iri_to_uri(request.get_full_path()))
26     cache_key = 'views.decorators.cache.cache_page.%s.%s.%s' % (
27         key_prefix, path.hexdigest(), ctx.hexdigest())
28     return _i18n_cache_key_suffix(request, cache_key)
29 
30 def _generate_cache_header_key(key_prefix, request):
31     """Returns a cache key for the header cache."""
32-    path = md5_constructor(iri_to_uri(request.path))
33+    path = md5_constructor(iri_to_uri(request.get_full_path()))
34     cache_key = 'views.decorators.cache.cache_header.%s.%s' % (
35         key_prefix, path.hexdigest())
36     return _i18n_cache_key_suffix(request, cache_key)
37 
38 def get_cache_key(request, key_prefix=None):
39     """
40-    Returns a cache key based on the request path. It can be used in the
41+    Returns a cache key based on the request path and query. It can be used in the
42     request phase because it pulls the list of headers to take into account
43     from the global path registry and uses those to build a cache key to check
44     against.
45@@ -206,7 +206,7 @@
46         return _generate_cache_key(request, headerlist, key_prefix)
47     else:
48         # if there is no Vary header, we still need a cache key
49-        # for the request.path
50+        # for the request.get_full_path()
51         cache.set(cache_key, [], cache_timeout)
52         return _generate_cache_key(request, [], key_prefix)
53 
54Index: django/middleware/cache.py
55===================================================================
56--- django/middleware/cache.py  (Revision 13963)
57+++ django/middleware/cache.py  (Arbeitskopie)
58@@ -23,7 +23,7 @@
59 
60 More details about how the caching works:
61 
62-* Only parameter-less GET or HEAD-requests with status code 200 are cached.
63+* Only GET or HEAD-requests with status code 200 are cached.
64 
65 * The number of seconds each page is stored for is set by the "max-age" section
66   of the response's "Cache-Control" header, falling back to the
67@@ -115,7 +115,7 @@
68         if self.cache_anonymous_only:
69             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."
70 
71-        if not request.method in ('GET', 'HEAD') or request.GET:
72+        if not request.method in ('GET', 'HEAD'):
73             request._cache_update_cache = False
74             return None # Don't bother checking the cache.
75 
76Index: django/http/__init__.py
77===================================================================
78--- django/http/__init__.py     (Revision 13963)
79+++ django/http/__init__.py     (Arbeitskopie)
80@@ -61,7 +61,10 @@
81         return host
82 
83     def get_full_path(self):
84-        return ''
85+        # RFC 3986 requires query string arguments to be in the ASCII range.
86+        # Rather than crash if this doesn't happen, we encode defensively.
87+        # taken from django/core/handlers/wsgi.py
88+        return '%s%s' % (self.path, self.META.get('QUERY_STRING', '') and ('?' + iri_to_uri(self.META.get('QUERY_STRING', ''))) or '')
89 
90     def build_absolute_uri(self, location=None):
91         """
92Index: tests/regressiontests/cache/tests.py
93===================================================================
94--- tests/regressiontests/cache/tests.py        (Revision 13963)
95+++ tests/regressiontests/cache/tests.py        (Arbeitskopie)
96@@ -14,7 +14,7 @@
97 from django.core import management
98 from django.core.cache import get_cache
99 from django.core.cache.backends.base import InvalidCacheBackendError, CacheKeyWarning
100-from django.http import HttpResponse, HttpRequest
101+from django.http import HttpResponse, HttpRequest, QueryDict
102 from django.middleware.cache import FetchFromCacheMiddleware, UpdateCacheMiddleware
103 from django.utils import translation
104 from django.utils.cache import patch_vary_headers, get_cache_key, learn_cache_key
105@@ -588,12 +588,15 @@
106         request.path = request.path_info = self.path
107         return request
108 
109-    def _get_request_cache(self):
110+    def _get_request_cache(self, query_string=None):
111         request = HttpRequest()
112         request.META = {
113             'SERVER_NAME': 'testserver',
114             'SERVER_PORT': 80,
115         }
116+        if query_string:
117+            request.META['QUERY_STRING']=query_string
118+            request.GET=QueryDict(query_string)
119         request.path = request.path_info = self.path
120         request._cache_update_cache = True
121         request.method = 'GET'
122@@ -629,9 +632,28 @@
123         settings.CACHE_MIDDLEWARE_KEY_PREFIX="test"
124         settings.CACHE_BACKEND='locmem:///'
125         settings.USE_I18N = True
126+
127+        # cache with non empty request.GET
128+        request = self._get_request_cache(query_string='foo=bar&other=true')
129+        get_cache_data = FetchFromCacheMiddleware().process_request(request)
130+        # first access, cache must return None
131+        self.assertEqual(get_cache_data, None)
132+        response = HttpResponse()
133+        content='Check for cache with QUERY_STRING'
134+        response.content=content
135+        UpdateCacheMiddleware().process_response(request, response)
136+        get_cache_data = FetchFromCacheMiddleware().process_request(request)
137+        # cache must return content
138+        self.assertNotEqual(get_cache_data, None) # request with QUERY_STRING not cached.
139+        self.assertEqual(get_cache_data.content, content)
140+        request = self._get_request_cache(query_string='foo=bar&somethingelse=true')
141+        get_cache_data = FetchFromCacheMiddleware().process_request(request)
142+        # different QUERY_STRING, cache must be empty
143+        self.assertEqual(get_cache_data, None)
144+
145+        # i18n tests
146         en_message ="Hello world!"
147         es_message ="Hola mundo!"
148-
149         request = self._get_request_cache()
150         set_cache(request, 'en', en_message)
151         get_cache_data = FetchFromCacheMiddleware().process_request(request)