summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorCarlton Gibson <carlton.gibson@noumenal.es>2022-04-07 07:05:59 +0200
committerGitHub <noreply@github.com>2022-04-07 07:05:59 +0200
commit9ffd4eae2ce7a7100c98f681e2b6ab818df384a4 (patch)
tree2cc678b6feff9f187517439bf2856a8702c1f356 /django
parent2ee4caf56b8e000cabbb73ad81ff05738d6d0a35 (diff)
Fixed #33611 -- Allowed View subclasses to define async method handlers.
Diffstat (limited to 'django')
-rw-r--r--django/views/generic/base.py33
1 files changed, 32 insertions, 1 deletions
diff --git a/django/views/generic/base.py b/django/views/generic/base.py
index d45b1762e6..db1842e3e5 100644
--- a/django/views/generic/base.py
+++ b/django/views/generic/base.py
@@ -1,3 +1,4 @@
+import asyncio
import logging
from django.core.exceptions import ImproperlyConfigured
@@ -11,6 +12,7 @@ from django.http import (
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils.decorators import classonlymethod
+from django.utils.functional import classproperty
logger = logging.getLogger("django.request")
@@ -57,6 +59,23 @@ class View:
for key, value in kwargs.items():
setattr(self, key, value)
+ @classproperty
+ def view_is_async(cls):
+ handlers = [
+ getattr(cls, method)
+ for method in cls.http_method_names
+ if (method != "options" and hasattr(cls, method))
+ ]
+ if not handlers:
+ return False
+ is_async = asyncio.iscoroutinefunction(handlers[0])
+ if not all(asyncio.iscoroutinefunction(h) == is_async for h in handlers[1:]):
+ raise ImproperlyConfigured(
+ f"{cls.__qualname__} HTTP handlers must either be all sync or all "
+ "async."
+ )
+ return is_async
+
@classonlymethod
def as_view(cls, **initkwargs):
"""Main entry point for a request-response process."""
@@ -96,6 +115,10 @@ class View:
# the dispatch method.
view.__dict__.update(cls.dispatch.__dict__)
+ # Mark the callback if the view class is async.
+ if cls.view_is_async:
+ view._is_coroutine = asyncio.coroutines._is_coroutine
+
return view
def setup(self, request, *args, **kwargs):
@@ -132,7 +155,15 @@ class View:
response = HttpResponse()
response.headers["Allow"] = ", ".join(self._allowed_methods())
response.headers["Content-Length"] = "0"
- return response
+
+ if self.view_is_async:
+
+ async def func():
+ return response
+
+ return func()
+ else:
+ return response
def _allowed_methods(self):
return [m.upper() for m in self.http_method_names if hasattr(self, m)]