summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntoine Lorence <contact@alorence.me>2022-09-29 12:10:02 +0200
committerCarlton Gibson <carlton@noumenal.es>2022-09-29 16:28:44 +0200
commit9b0c9821ed4dd9920cc7c5e7b657720d91a89bdc (patch)
treea812dd5f173c0080c3e49d7306511f82ab4d193d
parent19e6efa50b603af325e7f62058364f278596758f (diff)
downloaddjango-9b0c9821ed4dd9920cc7c5e7b657720d91a89bdc.tar.gz
Fixed #34062 -- Updated View.http_method_not_allowed() to support async.
As with the options() methods, wrap the response in a coroutine if the view is async. Co-authored-by: Carlton Gibson <carlton.gibson@noumenal.es>
-rw-r--r--django/views/generic/base.py11
-rw-r--r--docs/releases/4.1.2.txt4
-rw-r--r--tests/async/tests.py23
3 files changed, 35 insertions, 3 deletions
diff --git a/django/views/generic/base.py b/django/views/generic/base.py
index db1842e3e5..3a3afb0c73 100644
--- a/django/views/generic/base.py
+++ b/django/views/generic/base.py
@@ -148,7 +148,16 @@ class View:
request.path,
extra={"status_code": 405, "request": request},
)
- return HttpResponseNotAllowed(self._allowed_methods())
+ response = HttpResponseNotAllowed(self._allowed_methods())
+
+ if self.view_is_async:
+
+ async def func():
+ return response
+
+ return func()
+ else:
+ return response
def options(self, request, *args, **kwargs):
"""Handle responding to requests for the OPTIONS HTTP verb."""
diff --git a/docs/releases/4.1.2.txt b/docs/releases/4.1.2.txt
index 57b0b6c6e8..d607c34c92 100644
--- a/docs/releases/4.1.2.txt
+++ b/docs/releases/4.1.2.txt
@@ -39,3 +39,7 @@ Bugfixes
* Fixed a regression in Django 4.1 that didn't alter a sequence type when
altering type of pre-Django 4.1 serial columns on PostgreSQL
(:ticket:`34058`).
+
+* Fixed a regression in Django 4.1 that caused a crash for :class:`View`
+ subclasses with asynchronous handlers when handling non-allowed HTTP methods
+ (:ticket:`34062`).
diff --git a/tests/async/tests.py b/tests/async/tests.py
index 66eece4b97..559f21b8b1 100644
--- a/tests/async/tests.py
+++ b/tests/async/tests.py
@@ -6,8 +6,8 @@ from asgiref.sync import async_to_sync
from django.core.cache import DEFAULT_CACHE_ALIAS, caches
from django.core.exceptions import ImproperlyConfigured, SynchronousOnlyOperation
-from django.http import HttpResponse
-from django.test import SimpleTestCase
+from django.http import HttpResponse, HttpResponseNotAllowed
+from django.test import RequestFactory, SimpleTestCase
from django.utils.asyncio import async_unsafe
from django.views.generic.base import View
@@ -119,6 +119,25 @@ class ViewTests(SimpleTestCase):
self.assertIsInstance(response, HttpResponse)
+ def test_http_method_not_allowed_responds_correctly(self):
+ request_factory = RequestFactory()
+ tests = [
+ (SyncView, False),
+ (AsyncView, True),
+ ]
+ for view_cls, is_coroutine in tests:
+ with self.subTest(view_cls=view_cls, is_coroutine=is_coroutine):
+ instance = view_cls()
+ response = instance.http_method_not_allowed(request_factory.post("/"))
+ self.assertIs(
+ asyncio.iscoroutine(response),
+ is_coroutine,
+ )
+ if is_coroutine:
+ response = asyncio.run(response)
+
+ self.assertIsInstance(response, HttpResponseNotAllowed)
+
def test_base_view_class_is_sync(self):
"""
View and by extension any subclasses that don't define handlers are