#37065: @method_decorator can't be used on async view at class-level with
name="dispatch"
-----------------------------+--------------------------------------
     Reporter:  Jacob Walls  |                     Type:  Bug
       Status:  new          |                Component:  Utilities
      Version:  5.2          |                 Severity:  Normal
     Keywords:  async        |             Triage Stage:  Unreviewed
    Has patch:  0            |      Needs documentation:  0
  Needs tests:  0            |  Patch needs improvement:  0
Easy pickings:  0            |                    UI/UX:  0
-----------------------------+--------------------------------------
 `@method_decorator(..., name="dispatch")` doesn't work for async views
 that don't override `dispatch()`, because `dispatch()` is sync, which is
 enough to fool the logic in #35083.


 {{{#!diff
 diff --git a/tests/decorators/test_cache.py
 b/tests/decorators/test_cache.py
 index 1aca6967e0..c3b1668a7c 100644
 --- a/tests/decorators/test_cache.py
 +++ b/tests/decorators/test_cache.py
 @@ -1,3 +1,4 @@
 +from http import HTTPStatus
  from inspect import iscoroutinefunction
  from unittest import mock

 @@ -5,6 +6,7 @@ from django.http import HttpRequest, HttpResponse
  from django.test import SimpleTestCase
  from django.utils.decorators import method_decorator
  from django.views.decorators.cache import cache_control, cache_page,
 never_cache
 +from django.views.generic import View


  class HttpRequestProxy:
 @@ -217,6 +219,17 @@ class NeverCacheDecoratorTest(SimpleTestCase):
          with self.assertRaisesMessage(TypeError, msg):
              await MyClass().async_view(HttpRequestProxy(request))

 +    async def
 test_never_cache_method_decorator_http_request_async_view(self):
 +        @method_decorator(never_cache, name="dispatch")
 +        class MyClass(View):
 +            async def get(self, request):
 +                return HttpResponse()
 +
 +        request = HttpRequest()
 +        request.method = "GET"
 +        response = await MyClass().dispatch(request)
 +        self.assertEqual(response.status_code, HTTPStatus.OK)
 +
      def test_never_cache_decorator_http_request_proxy(self):
          class MyClass:
              @method_decorator(never_cache)
 }}}
 {{{#!py
 ERROR: test_never_cache_method_decorator_http_request_async_view
 
(decorators.test_cache.NeverCacheDecoratorTest.test_never_cache_method_decorator_http_request_async_view)
 ----------------------------------------------------------------------
 Traceback (most recent call last):
   File "/Users/jwalls/my314/lib/python3.14/site-packages/asgiref/sync.py",
 line 325, in __call__
     return call_result.result()
            ~~~~~~~~~~~~~~~~~~^^
   File
 
"/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/concurrent/futures/_base.py",
 line 443, in result
     return self.__get_result()
            ~~~~~~~~~~~~~~~~~^^
   File
 
"/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/concurrent/futures/_base.py",
 line 395, in __get_result
     raise self._exception
   File "/Users/jwalls/my314/lib/python3.14/site-packages/asgiref/sync.py",
 line 365, in main_wrap
     result = await awaitable
              ^^^^^^^^^^^^^^^
   File "/Users/jwalls/django/tests/decorators/test_cache.py", line 230, in
 test_never_cache_method_decorator_http_request_async_view
     response = await MyClass().dispatch(request)
                      ~~~~~~~~~~~~~~~~~~^^^^^^^^^
   File "/Users/jwalls/django/django/utils/decorators.py", line 47, in
 _wrapper
     return bound_method(*args, **kwargs)
   File "/Users/jwalls/django/django/views/decorators/cache.py", line 80,
 in _view_wrapper
     add_never_cache_headers(response)
     ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
   File "/Users/jwalls/django/django/utils/cache.py", line 294, in
 add_never_cache_headers
     patch_response_headers(response, cache_timeout=-1)
     ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/Users/jwalls/django/django/utils/cache.py", line 285, in
 patch_response_headers
     if not response.has_header("Expires"):
            ^^^^^^^^^^^^^^^^^^^
 AttributeError: 'coroutine' object has no attribute 'has_header'
 }}}

 ----
 Moving the decorator to the method instead of the class works. We probably
 will prefer a test inside the test class added in #35083 rather than
 merging my sketched test.
-- 
Ticket URL: <https://code.djangoproject.com/ticket/37065>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/0107019dc104043e-1d3edabc-240f-49de-aad5-51be450f1ab6-000000%40eu-central-1.amazonses.com.

Reply via email to