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, request.path,
extra={"status_code": 405, "request": request}, 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): def options(self, request, *args, **kwargs):
"""Handle responding to requests for the OPTIONS HTTP verb.""" """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 * 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 altering type of pre-Django 4.1 serial columns on PostgreSQL
(:ticket:`34058`). (: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.cache import DEFAULT_CACHE_ALIAS, caches
from django.core.exceptions import ImproperlyConfigured, SynchronousOnlyOperation from django.core.exceptions import ImproperlyConfigured, SynchronousOnlyOperation
from django.http import HttpResponse from django.http import HttpResponse, HttpResponseNotAllowed
from django.test import SimpleTestCase from django.test import RequestFactory, SimpleTestCase
from django.utils.asyncio import async_unsafe from django.utils.asyncio import async_unsafe
from django.views.generic.base import View from django.views.generic.base import View
@ -119,6 +119,25 @@ class ViewTests(SimpleTestCase):
self.assertIsInstance(response, HttpResponse) 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): def test_base_view_class_is_sync(self):
""" """
View and by extension any subclasses that don't define handlers are View and by extension any subclasses that don't define handlers are