1
0
mirror of https://github.com/django/django.git synced 2025-01-03 15:06:09 +00:00

Refs #31949 -- Made @csrf_exempt decorator to work with async functions.

This commit is contained in:
Ben Lomax 2023-07-08 21:00:42 +01:00 committed by Mariusz Felisiak
parent 6d427288e4
commit 953f81e078
5 changed files with 58 additions and 5 deletions

View File

@ -1,5 +1,7 @@
from functools import wraps
from asgiref.sync import iscoroutinefunction
from django.middleware.csrf import CsrfViewMiddleware, get_token
from django.utils.decorators import decorator_from_middleware
@ -51,9 +53,17 @@ def csrf_exempt(view_func):
# view_func.csrf_exempt = True would also work, but decorators are nicer
# if they don't have side effects, so return a new function.
@wraps(view_func)
def wrapper_view(*args, **kwargs):
return view_func(*args, **kwargs)
wrapper_view.csrf_exempt = True
return wrapper_view
if iscoroutinefunction(view_func):
async def _view_wrapper(request, *args, **kwargs):
return await view_func(request, *args, **kwargs)
else:
def _view_wrapper(request, *args, **kwargs):
return view_func(request, *args, **kwargs)
_view_wrapper.csrf_exempt = True
return wraps(view_func)(_view_wrapper)

View File

@ -150,6 +150,10 @@ class-based views<decorating-class-based-views>`.
def my_view(request):
return HttpResponse("Hello world")
.. versionchanged:: 5.0
Support for wrapping asynchronous view functions was added.
.. function:: csrf_protect(view)
Decorator that provides the protection of ``CsrfViewMiddleware`` to a view.

View File

@ -258,6 +258,7 @@ Decorators
* :func:`~django.views.decorators.cache.cache_control`
* :func:`~django.views.decorators.cache.never_cache`
* :func:`~django.views.decorators.common.no_append_slash`
* :func:`~django.views.decorators.csrf.csrf_exempt`
* :func:`~django.views.decorators.debug.sensitive_variables`
* :func:`~django.views.decorators.debug.sensitive_post_parameters`
* :func:`~django.views.decorators.http.condition`

View File

@ -84,6 +84,7 @@ view functions:
* :func:`~django.views.decorators.cache.cache_control`
* :func:`~django.views.decorators.cache.never_cache`
* :func:`~django.views.decorators.common.no_append_slash`
* :func:`~django.views.decorators.csrf.csrf_exempt`
* :func:`~django.views.decorators.http.condition`
* :func:`~django.views.decorators.http.etag`
* :func:`~django.views.decorators.http.last_modified`

View File

@ -0,0 +1,37 @@
from asgiref.sync import iscoroutinefunction
from django.http import HttpRequest, HttpResponse
from django.test import SimpleTestCase
from django.views.decorators.csrf import csrf_exempt
class CsrfExemptTests(SimpleTestCase):
def test_wrapped_sync_function_is_not_coroutine_function(self):
def sync_view(request):
return HttpResponse()
wrapped_view = csrf_exempt(sync_view)
self.assertIs(iscoroutinefunction(wrapped_view), False)
def test_wrapped_async_function_is_coroutine_function(self):
async def async_view(request):
return HttpResponse()
wrapped_view = csrf_exempt(async_view)
self.assertIs(iscoroutinefunction(wrapped_view), True)
def test_csrf_exempt_decorator(self):
@csrf_exempt
def sync_view(request):
return HttpResponse()
self.assertIs(sync_view.csrf_exempt, True)
self.assertIsInstance(sync_view(HttpRequest()), HttpResponse)
async def test_csrf_exempt_decorator_async_view(self):
@csrf_exempt
async def async_view(request):
return HttpResponse()
self.assertIs(async_view.csrf_exempt, True)
self.assertIsInstance(await async_view(HttpRequest()), HttpResponse)