Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #16003 -- Restored compatibility of the admin when using USE_ET…

…AGS. Thanks for the initial patch, pterk.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16729 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 7cb140e6d86e9c592983ddb50bfd82480ecec7eb 1 parent 2189a8e
@jezdez jezdez authored
Showing with 126 additions and 8 deletions.
  1. +8 −1 django/utils/cache.py
  2. +118 −7 tests/regressiontests/cache/tests.py
View
9 django/utils/cache.py
@@ -92,6 +92,10 @@ def get_max_age(response):
except (ValueError, TypeError):
pass
+def _set_response_etag(response):
+ response['ETag'] = '"%s"' % hashlib.md5(response.content).hexdigest()
+ return response
+
def patch_response_headers(response, cache_timeout=None):
"""
Adds some useful headers to the given HttpResponse object:
@@ -107,7 +111,10 @@ def patch_response_headers(response, cache_timeout=None):
if cache_timeout < 0:
cache_timeout = 0 # Can't have max-age negative
if settings.USE_ETAGS and not response.has_header('ETag'):
- response['ETag'] = '"%s"' % hashlib.md5(response.content).hexdigest()
+ if hasattr(response, 'render') and callable(response.render):
+ response.add_post_render_callback(_set_response_etag)
+ else:
+ response = _set_response_etag(response)
if not response.has_header('Last-Modified'):
response['Last-Modified'] = http_date()
if not response.has_header('Expires'):
View
125 tests/regressiontests/cache/tests.py
@@ -2,6 +2,7 @@
# Unit tests for cache framework
# Uses whatever cache backend is set in the test settings file.
+from __future__ import with_statement
import hashlib
import os
@@ -13,14 +14,19 @@
from django.conf import settings
from django.core import management
from django.core.cache import get_cache, DEFAULT_CACHE_ALIAS
-from django.core.cache.backends.base import CacheKeyWarning, InvalidCacheBackendError
+from django.core.cache.backends.base import (CacheKeyWarning,
+ InvalidCacheBackendError)
from django.http import HttpResponse, HttpRequest, QueryDict
-from django.middleware.cache import FetchFromCacheMiddleware, UpdateCacheMiddleware, CacheMiddleware
-from django.test import RequestFactory
-from django.test.utils import get_warnings_state, restore_warnings_state
-from django.utils import translation
-from django.utils import unittest
-from django.utils.cache import patch_vary_headers, get_cache_key, learn_cache_key, patch_cache_control
+from django.middleware.cache import (FetchFromCacheMiddleware,
+ UpdateCacheMiddleware, CacheMiddleware)
+from django.template import Template
+from django.template.response import TemplateResponse
+from django.test import TestCase, RequestFactory
+from django.test.utils import (get_warnings_state, restore_warnings_state,
+ override_settings)
+from django.utils import translation, unittest
+from django.utils.cache import (patch_vary_headers, get_cache_key,
+ learn_cache_key, patch_cache_control, patch_response_headers)
from django.views.decorators.cache import cache_page
from regressiontests.cache.models import Poll, expensive_calculation
@@ -1491,5 +1497,110 @@ def test_view_decorator(self):
response = other_with_timeout_view(request, '18')
self.assertEqual(response.content, 'Hello World 18')
+
+class TestWithTemplateResponse(TestCase):
+ """
+ Tests various headers w/ TemplateResponse.
+
+ Most are probably redundant since they manipulate the same object
+ anyway but the Etag header is 'special' because it relies on the
+ content being complete (which is not necessarily always the case
+ with a TemplateResponse)
+ """
+ def setUp(self):
+ self.path = '/cache/test/'
+
+ def _get_request(self, path, method='GET'):
+ request = HttpRequest()
+ request.META = {
+ 'SERVER_NAME': 'testserver',
+ 'SERVER_PORT': 80,
+ }
+ request.method = method
+ request.path = request.path_info = "/cache/%s" % path
+ return request
+
+ def test_patch_vary_headers(self):
+ headers = (
+ # Initial vary, new headers, resulting vary.
+ (None, ('Accept-Encoding',), 'Accept-Encoding'),
+ ('Accept-Encoding', ('accept-encoding',), 'Accept-Encoding'),
+ ('Accept-Encoding', ('ACCEPT-ENCODING',), 'Accept-Encoding'),
+ ('Cookie', ('Accept-Encoding',), 'Cookie, Accept-Encoding'),
+ ('Cookie, Accept-Encoding', ('Accept-Encoding',), 'Cookie, Accept-Encoding'),
+ ('Cookie, Accept-Encoding', ('Accept-Encoding', 'cookie'), 'Cookie, Accept-Encoding'),
+ (None, ('Accept-Encoding', 'COOKIE'), 'Accept-Encoding, COOKIE'),
+ ('Cookie, Accept-Encoding', ('Accept-Encoding', 'cookie'), 'Cookie, Accept-Encoding'),
+ ('Cookie , Accept-Encoding', ('Accept-Encoding', 'cookie'), 'Cookie, Accept-Encoding'),
+ )
+ for initial_vary, newheaders, resulting_vary in headers:
+ response = TemplateResponse(HttpResponse(), Template("This is a test"))
+ if initial_vary is not None:
+ response['Vary'] = initial_vary
+ patch_vary_headers(response, newheaders)
+ self.assertEqual(response['Vary'], resulting_vary)
+
+ def test_get_cache_key(self):
+ request = self._get_request(self.path)
+ response = TemplateResponse(HttpResponse(), Template("This is a test"))
+ key_prefix = 'localprefix'
+ # Expect None if no headers have been set yet.
+ self.assertEqual(get_cache_key(request), None)
+ # Set headers to an empty list.
+ learn_cache_key(request, response)
+ self.assertEqual(get_cache_key(request), 'views.decorators.cache.cache_page.settingsprefix.GET.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e')
+ # Verify that a specified key_prefix is taken into account.
+ learn_cache_key(request, response, key_prefix=key_prefix)
+ self.assertEqual(get_cache_key(request, key_prefix=key_prefix), 'views.decorators.cache.cache_page.localprefix.GET.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e')
+
+ def test_get_cache_key_with_query(self):
+ request = self._get_request(self.path + '?test=1')
+ response = TemplateResponse(HttpResponse(), Template("This is a test"))
+ # Expect None if no headers have been set yet.
+ self.assertEqual(get_cache_key(request), None)
+ # Set headers to an empty list.
+ learn_cache_key(request, response)
+ # Verify that the querystring is taken into account.
+ self.assertEqual(get_cache_key(request), 'views.decorators.cache.cache_page.settingsprefix.GET.bd889c5a59603af44333ed21504db3cd.d41d8cd98f00b204e9800998ecf8427e')
+
+ @override_settings(USE_ETAGS=False)
+ def test_without_etag(self):
+ response = TemplateResponse(HttpResponse(), Template("This is a test"))
+ self.assertFalse(response.has_header('ETag'))
+ patch_response_headers(response)
+ self.assertFalse(response.has_header('ETag'))
+ response = response.render()
+ self.assertFalse(response.has_header('ETag'))
+
+ @override_settings(USE_ETAGS=True)
+ def test_with_etag(self):
+ response = TemplateResponse(HttpResponse(), Template("This is a test"))
+ self.assertFalse(response.has_header('ETag'))
+ patch_response_headers(response)
+ self.assertFalse(response.has_header('ETag'))
+ response = response.render()
+ self.assertTrue(response.has_header('ETag'))
+
+TestWithTemplateResponse = override_settings(
+ CACHE_MIDDLEWARE_KEY_PREFIX='settingsprefix',
+ CACHE_MIDDLEWARE_SECONDS=1,
+ USE_I18N=False,
+)(TestWithTemplateResponse)
+
+
+class TestEtagWithAdmin(TestCase):
+ # See https://code.djangoproject.com/ticket/16003
+ def test_admin(self):
+ with self.settings(USE_ETAGS=False):
+ response = self.client.get('/test_admin/admin/')
+ self.assertEqual(response.status_code, 200)
+ self.assertFalse(response.has_header('ETag'))
+
+ with self.settings(USE_ETAGS=True):
+ response = self.client.get('/test_admin/admin/')
+ self.assertEqual(response.status_code, 200)
+ self.assertTrue(response.has_header('ETag'))
+
if __name__ == '__main__':
unittest.main()
+
Please sign in to comment.
Something went wrong with that request. Please try again.