summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Lomax <lomax.on.the.run@gmail.com>2023-05-19 21:14:32 +0800
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-05-23 10:04:41 +0200
commit23abec9192c88f9372e5784ae69c9aa5be2b21be (patch)
tree7dcee84ff72a113563fb1eb4bbf47948e0ed305c
parente5c844d6f2a4ac6ae674d741b5f1fa2a688cedf4 (diff)
Refs #31949 -- Made @no_append_slash decorator to work with async functions.
-rw-r--r--django/views/decorators/common.py20
-rw-r--r--docs/releases/5.0.txt1
-rw-r--r--docs/topics/async.txt1
-rw-r--r--docs/topics/http/decorators.txt4
-rw-r--r--tests/decorators/test_common.py37
5 files changed, 58 insertions, 5 deletions
diff --git a/django/views/decorators/common.py b/django/views/decorators/common.py
index 71ee232ae4..d09b0bfee4 100644
--- a/django/views/decorators/common.py
+++ b/django/views/decorators/common.py
@@ -1,5 +1,7 @@
from functools import wraps
+from asgiref.sync import iscoroutinefunction
+
def no_append_slash(view_func):
"""
@@ -9,9 +11,17 @@ def no_append_slash(view_func):
# view_func.should_append_slash = False 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.should_append_slash = False
- 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.should_append_slash = False
+
+ return wraps(view_func)(_view_wrapper)
diff --git a/docs/releases/5.0.txt b/docs/releases/5.0.txt
index e6526dd798..6c16945f1f 100644
--- a/docs/releases/5.0.txt
+++ b/docs/releases/5.0.txt
@@ -237,6 +237,7 @@ Decorators
* :func:`~django.views.decorators.cache.cache_control`
* :func:`~django.views.decorators.cache.never_cache`
+ * :func:`~django.views.decorators.common.no_append_slash`
* ``xframe_options_deny()``
* ``xframe_options_sameorigin()``
* ``xframe_options_exempt()``
diff --git a/docs/topics/async.txt b/docs/topics/async.txt
index 444c88a1c6..73f4bf2a35 100644
--- a/docs/topics/async.txt
+++ b/docs/topics/async.txt
@@ -83,6 +83,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`
* ``xframe_options_deny()``
* ``xframe_options_sameorigin()``
* ``xframe_options_exempt()``
diff --git a/docs/topics/http/decorators.txt b/docs/topics/http/decorators.txt
index 4bcae64e27..49219f3d5a 100644
--- a/docs/topics/http/decorators.txt
+++ b/docs/topics/http/decorators.txt
@@ -147,3 +147,7 @@ customization of :class:`~django.middleware.common.CommonMiddleware` behavior.
This decorator allows individual views to be excluded from
:setting:`APPEND_SLASH` URL normalization.
+
+ .. versionchanged:: 5.0
+
+ Support for wrapping asynchronous view functions was added.
diff --git a/tests/decorators/test_common.py b/tests/decorators/test_common.py
new file mode 100644
index 0000000000..a2661ef30a
--- /dev/null
+++ b/tests/decorators/test_common.py
@@ -0,0 +1,37 @@
+from asgiref.sync import iscoroutinefunction
+
+from django.http import HttpRequest, HttpResponse
+from django.test import SimpleTestCase
+from django.views.decorators.common import no_append_slash
+
+
+class NoAppendSlashTests(SimpleTestCase):
+ def test_wrapped_sync_function_is_not_coroutine_function(self):
+ def sync_view(request):
+ return HttpResponse()
+
+ wrapped_view = no_append_slash(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 = no_append_slash(async_view)
+ self.assertIs(iscoroutinefunction(wrapped_view), True)
+
+ def test_no_append_slash_decorator(self):
+ @no_append_slash
+ def sync_view(request):
+ return HttpResponse()
+
+ self.assertIs(sync_view.should_append_slash, False)
+ self.assertIsInstance(sync_view(HttpRequest()), HttpResponse)
+
+ async def test_no_append_slash_decorator_async_view(self):
+ @no_append_slash
+ async def async_view(request):
+ return HttpResponse()
+
+ self.assertIs(async_view.should_append_slash, False)
+ self.assertIsInstance(await async_view(HttpRequest()), HttpResponse)