1
0
mirror of https://github.com/django/django.git synced 2025-10-24 06:06:09 +00:00

Fixed #33611 -- Allowed View subclasses to define async method handlers.

This commit is contained in:
Carlton Gibson
2022-04-07 07:05:59 +02:00
committed by GitHub
parent 2ee4caf56b
commit 9ffd4eae2c
6 changed files with 168 additions and 4 deletions

View File

@@ -1,3 +1,4 @@
import asyncio
import os
import sys
from unittest import mock, skipIf
@@ -5,9 +6,11 @@ from unittest import mock, skipIf
from asgiref.sync import async_to_sync
from django.core.cache import DEFAULT_CACHE_ALIAS, caches
from django.core.exceptions import SynchronousOnlyOperation
from django.core.exceptions import ImproperlyConfigured, SynchronousOnlyOperation
from django.http import HttpResponse
from django.test import SimpleTestCase
from django.utils.asyncio import async_unsafe
from django.views.generic.base import View
from .models import SimpleModel
@@ -72,3 +75,66 @@ class AsyncUnsafeTest(SimpleTestCase):
self.dangerous_method()
except SynchronousOnlyOperation:
self.fail("SynchronousOnlyOperation should not be raised.")
class SyncView(View):
def get(self, request, *args, **kwargs):
return HttpResponse("Hello (sync) world!")
class AsyncView(View):
async def get(self, request, *args, **kwargs):
return HttpResponse("Hello (async) world!")
class ViewTests(SimpleTestCase):
def test_views_are_correctly_marked(self):
tests = [
(SyncView, False),
(AsyncView, True),
]
for view_cls, is_async in tests:
with self.subTest(view_cls=view_cls, is_async=is_async):
self.assertIs(view_cls.view_is_async, is_async)
callback = view_cls.as_view()
self.assertIs(asyncio.iscoroutinefunction(callback), is_async)
def test_mixed_views_raise_error(self):
class MixedView(View):
def get(self, request, *args, **kwargs):
return HttpResponse("Hello (mixed) world!")
async def post(self, request, *args, **kwargs):
return HttpResponse("Hello (mixed) world!")
msg = (
f"{MixedView.__qualname__} HTTP handlers must either be all sync or all "
"async."
)
with self.assertRaisesMessage(ImproperlyConfigured, msg):
MixedView.as_view()
def test_options_handler_responds_correctly(self):
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.options(None)
self.assertIs(
asyncio.iscoroutine(response),
is_coroutine,
)
if is_coroutine:
response = asyncio.run(response)
self.assertIsInstance(response, HttpResponse)
def test_base_view_class_is_sync(self):
"""
View and by extension any subclasses that don't define handlers are
sync.
"""
self.assertIs(View.view_is_async, False)