Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #4992 -- Respect the GET request query string when creating cac…

…he keys. Thanks PeterKz and guettli for the initial patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15705 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit f6c991667f6f1ab8d2fd2afc1ee2b5801e78b7e3 1 parent a9ace14
Jannis Leidel authored March 02, 2011
4  django/http/__init__.py
@@ -166,7 +166,9 @@ def get_host(self):
166 166
         return host
167 167
 
168 168
     def get_full_path(self):
169  
-        return ''
  169
+        # RFC 3986 requires query string arguments to be in the ASCII range.
  170
+        # Rather than crash if this doesn't happen, we encode defensively.
  171
+        return '%s%s' % (self.path, self.META.get('QUERY_STRING', '') and ('?' + iri_to_uri(self.META.get('QUERY_STRING', ''))) or '')
170 172
 
171 173
     def build_absolute_uri(self, location=None):
172 174
         """
4  django/middleware/cache.py
@@ -23,7 +23,7 @@
23 23
 
24 24
 More details about how the caching works:
25 25
 
26  
-* Only parameter-less GET or HEAD-requests with status code 200 are cached.
  26
+* Only GET or HEAD-requests with status code 200 are cached.
27 27
 
28 28
 * The number of seconds each page is stored for is set by the "max-age" section
29 29
   of the response's "Cache-Control" header, falling back to the
@@ -135,7 +135,7 @@ def process_request(self, request):
135 135
         Checks whether the page is already cached and returns the cached
136 136
         version if available.
137 137
         """
138  
-        if not request.method in ('GET', 'HEAD') or request.GET:
  138
+        if not request.method in ('GET', 'HEAD'):
139 139
             request._cache_update_cache = False
140 140
             return None # Don't bother checking the cache.
141 141
 
14  django/utils/cache.py
@@ -160,24 +160,24 @@ def _generate_cache_key(request, method, headerlist, key_prefix):
160 160
         value = request.META.get(header, None)
161 161
         if value is not None:
162 162
             ctx.update(value)
163  
-    path = md5_constructor(iri_to_uri(request.path))
  163
+    path = md5_constructor(iri_to_uri(request.get_full_path()))
164 164
     cache_key = 'views.decorators.cache.cache_page.%s.%s.%s.%s' % (
165 165
         key_prefix, request.method, path.hexdigest(), ctx.hexdigest())
166 166
     return _i18n_cache_key_suffix(request, cache_key)
167 167
 
168 168
 def _generate_cache_header_key(key_prefix, request):
169 169
     """Returns a cache key for the header cache."""
170  
-    path = md5_constructor(iri_to_uri(request.path))
  170
+    path = md5_constructor(iri_to_uri(request.get_full_path()))
171 171
     cache_key = 'views.decorators.cache.cache_header.%s.%s' % (
172 172
         key_prefix, path.hexdigest())
173 173
     return _i18n_cache_key_suffix(request, cache_key)
174 174
 
175 175
 def get_cache_key(request, key_prefix=None, method='GET', cache=None):
176 176
     """
177  
-    Returns a cache key based on the request path. It can be used in the
178  
-    request phase because it pulls the list of headers to take into account
179  
-    from the global path registry and uses those to build a cache key to check
180  
-    against.
  177
+    Returns a cache key based on the request path and query. It can be used
  178
+    in the request phase because it pulls the list of headers to take into
  179
+    account from the global path registry and uses those to build a cache key
  180
+    to check against.
181 181
 
182 182
     If there is no headerlist stored, the page needs to be rebuilt, so this
183 183
     function returns None.
@@ -220,7 +220,7 @@ def learn_cache_key(request, response, cache_timeout=None, key_prefix=None, cach
220 220
         return _generate_cache_key(request, request.method, headerlist, key_prefix)
221 221
     else:
222 222
         # if there is no Vary header, we still need a cache key
223  
-        # for the request.path
  223
+        # for the request.get_full_path()
224 224
         cache.set(cache_key, [], cache_timeout)
225 225
         return _generate_cache_key(request, request.method, [], key_prefix)
226 226
 
3  docs/releases/1.3.txt
@@ -169,6 +169,9 @@ Secondly, :ref:`Versioning <cache_versioning>`, :ref:`site-wide
169 169
 prefixing <cache_key_prefixing>` and :ref:`transformation
170 170
 <cache_key_transformation>` has been added to the cache API.
171 171
 
  172
+Thirdly, the :ref:`cache key creation <using-vary-headers>` has been
  173
+updated to take the GET request query string into account.
  174
+
172 175
 Lastly, support for pylibmc_ has been added to the memcached cache
173 176
 backend.
174 177
 
8  docs/topics/cache.txt
@@ -962,9 +962,13 @@ mechanism should take into account when building its cache key. For example, if
962 962
 the contents of a Web page depend on a user's language preference, the page is
963 963
 said to "vary on language."
964 964
 
  965
+.. versionchanged:: 1.3
  966
+    In Django 1.3 the full request path -- including the query -- is used
  967
+    to create the cache keys, instead of only the path component in Django 1.2.
  968
+
965 969
 By default, Django's cache system creates its cache keys using the requested
966  
-path (e.g., ``"/stories/2005/jun/23/bank_robbed/"``). This means every request
967  
-to that URL will use the same cached version, regardless of user-agent
  970
+path and query -- e.g., ``"/stories/2005/?order_by=author"``. This means every
  971
+request to that URL will use the same cached version, regardless of user-agent
968 972
 differences such as cookies or language preferences. However, if this page
969 973
 produces different content based on some difference in request headers -- such
970 974
 as a cookie, or a language, or a user-agent -- you'll need to use the ``Vary``
39  tests/regressiontests/cache/tests.py
@@ -12,7 +12,7 @@
12 12
 from django.core import management
13 13
 from django.core.cache import get_cache, DEFAULT_CACHE_ALIAS
14 14
 from django.core.cache.backends.base import CacheKeyWarning
15  
-from django.http import HttpResponse, HttpRequest
  15
+from django.http import HttpResponse, HttpRequest, QueryDict
16 16
 from django.middleware.cache import FetchFromCacheMiddleware, UpdateCacheMiddleware, CacheMiddleware
17 17
 from django.test import RequestFactory
18 18
 from django.test.utils import get_warnings_state, restore_warnings_state
@@ -920,10 +920,20 @@ def test_get_cache_key(self):
920 920
         # Set headers to an empty list.
921 921
         learn_cache_key(request, response)
922 922
         self.assertEqual(get_cache_key(request), 'views.decorators.cache.cache_page.settingsprefix.GET.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e')
923  
-        # Verify that a specified key_prefix is taken in to account.
  923
+        # Verify that a specified key_prefix is taken into account.
924 924
         learn_cache_key(request, response, key_prefix=key_prefix)
925 925
         self.assertEqual(get_cache_key(request, key_prefix=key_prefix), 'views.decorators.cache.cache_page.localprefix.GET.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e')
926 926
 
  927
+    def test_get_cache_key_with_query(self):
  928
+        request = self._get_request(self.path + '?test=1')
  929
+        response = HttpResponse()
  930
+        # Expect None if no headers have been set yet.
  931
+        self.assertEqual(get_cache_key(request), None)
  932
+        # Set headers to an empty list.
  933
+        learn_cache_key(request, response)
  934
+        # Verify that the querystring is taken into account.
  935
+        self.assertEqual(get_cache_key(request), 'views.decorators.cache.cache_page.settingsprefix.GET.bd889c5a59603af44333ed21504db3cd.d41d8cd98f00b204e9800998ecf8427e')
  936
+
927 937
     def test_learn_cache_key(self):
928 938
         request = self._get_request(self.path, 'HEAD')
929 939
         response = HttpResponse()
@@ -1039,12 +1049,15 @@ def _get_request(self):
1039 1049
         request.path = request.path_info = self.path
1040 1050
         return request
1041 1051
 
1042  
-    def _get_request_cache(self):
  1052
+    def _get_request_cache(self, query_string=None):
1043 1053
         request = HttpRequest()
1044 1054
         request.META = {
1045 1055
             'SERVER_NAME': 'testserver',
1046 1056
             'SERVER_PORT': 80,
1047 1057
         }
  1058
+        if query_string:
  1059
+            request.META['QUERY_STRING'] = query_string
  1060
+            request.GET = QueryDict(query_string)
1048 1061
         request.path = request.path_info = self.path
1049 1062
         request._cache_update_cache = True
1050 1063
         request.method = 'GET'
@@ -1085,6 +1098,26 @@ def set_cache(request, lang, msg):
1085 1098
         }
1086 1099
         settings.USE_ETAGS = True
1087 1100
         settings.USE_I18N = True
  1101
+
  1102
+        # cache with non empty request.GET
  1103
+        request = self._get_request_cache(query_string='foo=bar&other=true')
  1104
+        get_cache_data = FetchFromCacheMiddleware().process_request(request)
  1105
+        # first access, cache must return None
  1106
+        self.assertEqual(get_cache_data, None)
  1107
+        response = HttpResponse()
  1108
+        content = 'Check for cache with QUERY_STRING'
  1109
+        response.content = content
  1110
+        UpdateCacheMiddleware().process_response(request, response)
  1111
+        get_cache_data = FetchFromCacheMiddleware().process_request(request)
  1112
+        # cache must return content
  1113
+        self.assertNotEqual(get_cache_data, None)
  1114
+        self.assertEqual(get_cache_data.content, content)
  1115
+        # different QUERY_STRING, cache must be empty
  1116
+        request = self._get_request_cache(query_string='foo=bar&somethingelse=true')
  1117
+        get_cache_data = FetchFromCacheMiddleware().process_request(request)
  1118
+        self.assertEqual(get_cache_data, None)
  1119
+
  1120
+        # i18n tests
1088 1121
         en_message ="Hello world!"
1089 1122
         es_message ="Hola mundo!"
1090 1123
 

0 notes on commit f6c9916

Please sign in to comment.
Something went wrong with that request. Please try again.