[Django] #35083: @method_decorator and iscoroutinefunction() fail to interact properly on async views

3 views
Skip to first unread message

Django

unread,
Jan 3, 2024, 9:28:38 AMJan 3
to django-...@googlegroups.com
#35083: @method_decorator and iscoroutinefunction() fail to interact properly on
async views
-----------------------------------------+------------------------
Reporter: Drew Winstel | Owner: nobody
Type: Uncategorized | Status: new
Component: Uncategorized | Version: 5.0
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 |
-----------------------------------------+------------------------
I first discovered this while trying to enforce a CSRF cookie on a
subclass of the [standard strawberry GraphQL
view](https://strawberry.rocks/docs/integrations/django), but I was able
to replicate this using exclusively plain Django code:

When trying to use a `@method_decorator` to enforce a CSRF cookie on a
parent class's method, the `iscoroutinefunction` logic appears to
incorrectly detect that the decorated view is indeed async
(https://github.com/django/django/blob/74f7deec9e334a69bfbfdd068285618534198bd5/django/utils/decorators.py#L162-L192),
leading to an exception: `AttributeError: 'coroutine' object has no
attribute 'set_cookie'`

Steps to replicate:
1. Create a simple project with the below views and URLs.
2. GET `/view1/` and observe that you see "hi" in the response
3. GET `/view2/` and observe the above attribute error

Expected behavior:
- Steps 2 and 3 return identical results

{{{
# views.py
from typing import Any

from django.http.request import HttpRequest
from django.http.response import HttpResponse
from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import ensure_csrf_cookie


class View1(View):
@method_decorator(ensure_csrf_cookie)
async def dispatch(
self, request: HttpRequest, *args: Any, **kwargs: Any
) -> HttpResponse:
return await super().dispatch(request, *args, **kwargs)

async def get(self, request: HttpRequest):
return HttpResponse("hi")


@method_decorator(ensure_csrf_cookie, name="dispatch")
class View2(View1):
pass
}}}

{{{
# urls.py
import views

# snip
urlpatterns += [
path("/view1/", views.View1.as_view()),
path("/view2/", view.View2.as_view()),
]
}}}

Thanks!

--
Ticket URL: <https://code.djangoproject.com/ticket/35083>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Reply all
Reply to author
Forward
0 new messages