diff --git a/django/views/decorators/cache.py b/django/views/decorators/cache.py index 773cf0c2c6..fdc5917738 100644 --- a/django/views/decorators/cache.py +++ b/django/views/decorators/cache.py @@ -1,5 +1,6 @@ from functools import wraps +from django.http import HttpRequest from django.middleware.cache import CacheMiddleware from django.utils.cache import add_never_cache_headers, patch_cache_control from django.utils.decorators import decorator_from_middleware_with_args @@ -28,6 +29,12 @@ def cache_control(**kwargs): def _cache_controller(viewfunc): @wraps(viewfunc) def _cache_controlled(request, *args, **kw): + if not isinstance(request, HttpRequest): + raise TypeError( + "cache_control didn't receive an HttpRequest. If you are " + "decorating a classmethod, be sure to use " + "@method_decorator." + ) response = viewfunc(request, *args, **kw) patch_cache_control(response, **kwargs) return response @@ -41,6 +48,11 @@ def never_cache(view_func): """ @wraps(view_func) def _wrapped_view_func(request, *args, **kwargs): + if not isinstance(request, HttpRequest): + raise TypeError( + "never_cache didn't receive an HttpRequest. If you are " + "decorating a classmethod, be sure to use @method_decorator." + ) response = view_func(request, *args, **kwargs) add_never_cache_headers(response) return response diff --git a/tests/decorators/tests.py b/tests/decorators/tests.py index 5bb21661e3..46b01c1852 100644 --- a/tests/decorators/tests.py +++ b/tests/decorators/tests.py @@ -470,7 +470,7 @@ class XFrameOptionsDecoratorsTests(TestCase): self.assertIsNone(r.get('X-Frame-Options', None)) -class NeverCacheDecoratorTest(TestCase): +class NeverCacheDecoratorTest(SimpleTestCase): def test_never_cache_decorator(self): @never_cache def a_view(request): @@ -480,3 +480,30 @@ class NeverCacheDecoratorTest(TestCase): set(r.headers['Cache-Control'].split(', ')), {'max-age=0', 'no-cache', 'no-store', 'must-revalidate', 'private'}, ) + + def test_never_cache_decorator_http_request(self): + class MyClass: + @never_cache + def a_view(self, request): + return HttpResponse() + msg = ( + "never_cache didn't receive an HttpRequest. If you are decorating " + "a classmethod, be sure to use @method_decorator." + ) + with self.assertRaisesMessage(TypeError, msg): + MyClass().a_view(HttpRequest()) + + +class CacheControlDecoratorTest(SimpleTestCase): + def test_cache_control_decorator_http_request(self): + class MyClass: + @cache_control(a='b') + def a_view(self, request): + return HttpResponse() + + msg = ( + "cache_control didn't receive an HttpRequest. If you are " + "decorating a classmethod, be sure to use @method_decorator." + ) + with self.assertRaisesMessage(TypeError, msg): + MyClass().a_view(HttpRequest())