Fixed #33350 -- Reallowed using cache decorators with duck-typed HttpRequest.
Regression in 3fd82a6241
.
Thanks Terence Honles for the report.
This commit is contained in:
parent
068b2c072b
commit
40165eecc4
|
@ -1,6 +1,5 @@
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from django.http import HttpRequest
|
|
||||||
from django.middleware.cache import CacheMiddleware
|
from django.middleware.cache import CacheMiddleware
|
||||||
from django.utils.cache import add_never_cache_headers, patch_cache_control
|
from django.utils.cache import add_never_cache_headers, patch_cache_control
|
||||||
from django.utils.decorators import decorator_from_middleware_with_args
|
from django.utils.decorators import decorator_from_middleware_with_args
|
||||||
|
@ -29,7 +28,8 @@ def cache_control(**kwargs):
|
||||||
def _cache_controller(viewfunc):
|
def _cache_controller(viewfunc):
|
||||||
@wraps(viewfunc)
|
@wraps(viewfunc)
|
||||||
def _cache_controlled(request, *args, **kw):
|
def _cache_controlled(request, *args, **kw):
|
||||||
if not isinstance(request, HttpRequest):
|
# Ensure argument looks like a request.
|
||||||
|
if not hasattr(request, 'META'):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"cache_control didn't receive an HttpRequest. If you are "
|
"cache_control didn't receive an HttpRequest. If you are "
|
||||||
"decorating a classmethod, be sure to use "
|
"decorating a classmethod, be sure to use "
|
||||||
|
@ -48,7 +48,8 @@ def never_cache(view_func):
|
||||||
"""
|
"""
|
||||||
@wraps(view_func)
|
@wraps(view_func)
|
||||||
def _wrapped_view_func(request, *args, **kwargs):
|
def _wrapped_view_func(request, *args, **kwargs):
|
||||||
if not isinstance(request, HttpRequest):
|
# Ensure argument looks like a request.
|
||||||
|
if not hasattr(request, 'META'):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"never_cache didn't receive an HttpRequest. If you are "
|
"never_cache didn't receive an HttpRequest. If you are "
|
||||||
"decorating a classmethod, be sure to use @method_decorator."
|
"decorating a classmethod, be sure to use @method_decorator."
|
||||||
|
|
|
@ -15,3 +15,7 @@ Bugfixes
|
||||||
|
|
||||||
* Fixed a bug in Django 4.0 that caused a crash on booleans with the
|
* Fixed a bug in Django 4.0 that caused a crash on booleans with the
|
||||||
``RedisCache`` backend (:ticket:`33361`).
|
``RedisCache`` backend (:ticket:`33361`).
|
||||||
|
|
||||||
|
* Relaxed the check added in Django 4.0 to reallow use of a duck-typed
|
||||||
|
``HttpRequest`` in ``django.views.decorators.cache.cache_control()`` and
|
||||||
|
``never_cache()`` decorators (:ticket:`33350`).
|
||||||
|
|
|
@ -493,6 +493,15 @@ class XFrameOptionsDecoratorsTests(TestCase):
|
||||||
self.assertIsNone(r.get('X-Frame-Options', None))
|
self.assertIsNone(r.get('X-Frame-Options', None))
|
||||||
|
|
||||||
|
|
||||||
|
class HttpRequestProxy:
|
||||||
|
def __init__(self, request):
|
||||||
|
self._request = request
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
"""Proxy to the underlying HttpRequest object."""
|
||||||
|
return getattr(self._request, attr)
|
||||||
|
|
||||||
|
|
||||||
class NeverCacheDecoratorTest(SimpleTestCase):
|
class NeverCacheDecoratorTest(SimpleTestCase):
|
||||||
|
|
||||||
@mock.patch('time.time')
|
@mock.patch('time.time')
|
||||||
|
@ -525,12 +534,27 @@ class NeverCacheDecoratorTest(SimpleTestCase):
|
||||||
@never_cache
|
@never_cache
|
||||||
def a_view(self, request):
|
def a_view(self, request):
|
||||||
return HttpResponse()
|
return HttpResponse()
|
||||||
|
|
||||||
|
request = HttpRequest()
|
||||||
msg = (
|
msg = (
|
||||||
"never_cache didn't receive an HttpRequest. If you are decorating "
|
"never_cache didn't receive an HttpRequest. If you are decorating "
|
||||||
"a classmethod, be sure to use @method_decorator."
|
"a classmethod, be sure to use @method_decorator."
|
||||||
)
|
)
|
||||||
with self.assertRaisesMessage(TypeError, msg):
|
with self.assertRaisesMessage(TypeError, msg):
|
||||||
MyClass().a_view(HttpRequest())
|
MyClass().a_view(request)
|
||||||
|
with self.assertRaisesMessage(TypeError, msg):
|
||||||
|
MyClass().a_view(HttpRequestProxy(request))
|
||||||
|
|
||||||
|
def test_never_cache_decorator_http_request_proxy(self):
|
||||||
|
class MyClass:
|
||||||
|
@method_decorator(never_cache)
|
||||||
|
def a_view(self, request):
|
||||||
|
return HttpResponse()
|
||||||
|
|
||||||
|
request = HttpRequest()
|
||||||
|
response = MyClass().a_view(HttpRequestProxy(request))
|
||||||
|
self.assertIn('Cache-Control', response.headers)
|
||||||
|
self.assertIn('Expires', response.headers)
|
||||||
|
|
||||||
|
|
||||||
class CacheControlDecoratorTest(SimpleTestCase):
|
class CacheControlDecoratorTest(SimpleTestCase):
|
||||||
|
@ -544,5 +568,18 @@ class CacheControlDecoratorTest(SimpleTestCase):
|
||||||
"cache_control didn't receive an HttpRequest. If you are "
|
"cache_control didn't receive an HttpRequest. If you are "
|
||||||
"decorating a classmethod, be sure to use @method_decorator."
|
"decorating a classmethod, be sure to use @method_decorator."
|
||||||
)
|
)
|
||||||
|
request = HttpRequest()
|
||||||
with self.assertRaisesMessage(TypeError, msg):
|
with self.assertRaisesMessage(TypeError, msg):
|
||||||
MyClass().a_view(HttpRequest())
|
MyClass().a_view(request)
|
||||||
|
with self.assertRaisesMessage(TypeError, msg):
|
||||||
|
MyClass().a_view(HttpRequestProxy(request))
|
||||||
|
|
||||||
|
def test_cache_control_decorator_http_request_proxy(self):
|
||||||
|
class MyClass:
|
||||||
|
@method_decorator(cache_control(a='b'))
|
||||||
|
def a_view(self, request):
|
||||||
|
return HttpResponse()
|
||||||
|
|
||||||
|
request = HttpRequest()
|
||||||
|
response = MyClass().a_view(HttpRequestProxy(request))
|
||||||
|
self.assertEqual(response.headers['Cache-Control'], 'a=b')
|
||||||
|
|
Loading…
Reference in New Issue