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>
This commit is contained in:
Antoine Lorence 2022-09-29 12:10:02 +02:00 committed by Carlton Gibson
parent 19e6efa50b
commit 9b0c9821ed
3 changed files with 35 additions and 3 deletions

View File

@ -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."""

View File

@ -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`).

View File

@ -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