mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-07-27 08:29:59 +03:00
Updated docstring and added support for async function based views
This commit is contained in:
parent
ae84d97d9a
commit
351ea7d61f
|
@ -6,6 +6,7 @@ There are also various decorators for setting the API policies on function
|
||||||
based views, as well as the `@action` decorator, which is used to annotate
|
based views, as well as the `@action` decorator, which is used to annotate
|
||||||
methods on viewsets that should be included by routers.
|
methods on viewsets that should be included by routers.
|
||||||
"""
|
"""
|
||||||
|
import asyncio
|
||||||
import types
|
import types
|
||||||
|
|
||||||
from django.forms.utils import pretty_name
|
from django.forms.utils import pretty_name
|
||||||
|
@ -46,11 +47,19 @@ def api_view(http_method_names=None):
|
||||||
allowed_methods = set(http_method_names) | {'options'}
|
allowed_methods = set(http_method_names) | {'options'}
|
||||||
WrappedAPIView.http_method_names = [method.lower() for method in allowed_methods]
|
WrappedAPIView.http_method_names = [method.lower() for method in allowed_methods]
|
||||||
|
|
||||||
def handler(self, *args, **kwargs):
|
def sync_handler(self, *args, **kwargs):
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
|
async def async_handler(self, *args, **kwargs):
|
||||||
|
return await func(*args, **kwargs)
|
||||||
|
|
||||||
|
view_is_async = asyncio.iscoroutinefunction(func)
|
||||||
|
|
||||||
for method in http_method_names:
|
for method in http_method_names:
|
||||||
setattr(WrappedAPIView, method.lower(), handler)
|
if view_is_async:
|
||||||
|
setattr(WrappedAPIView, method.lower(), async_handler)
|
||||||
|
else:
|
||||||
|
setattr(WrappedAPIView, method.lower(), sync_handler)
|
||||||
|
|
||||||
WrappedAPIView.__name__ = func.__name__
|
WrappedAPIView.__name__ = func.__name__
|
||||||
WrappedAPIView.__module__ = func.__module__
|
WrappedAPIView.__module__ = func.__module__
|
||||||
|
|
|
@ -484,7 +484,7 @@ class APIView(View):
|
||||||
# be overridden.
|
# be overridden.
|
||||||
def sync_dispatch(self, request, *args, **kwargs):
|
def sync_dispatch(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
`.dispatch()` is pretty much the same as Django's regular dispatch,
|
`.sync_dispatch()` is pretty much the same as Django's regular dispatch,
|
||||||
but with extra hooks for startup, finalize, and exception handling.
|
but with extra hooks for startup, finalize, and exception handling.
|
||||||
"""
|
"""
|
||||||
self.args = args
|
self.args = args
|
||||||
|
@ -513,8 +513,9 @@ class APIView(View):
|
||||||
|
|
||||||
async def async_dispatch(self, request, *args, **kwargs):
|
async def async_dispatch(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
`.dispatch()` is pretty much the same as Django's regular dispatch,
|
`.async_dispatch()` is pretty much the same as Django's regular dispatch,
|
||||||
but with extra hooks for startup, finalize, and exception handling.
|
except for awaiting the handler function and with extra hooks for startup,
|
||||||
|
finalize, and exception handling.
|
||||||
"""
|
"""
|
||||||
self.args = args
|
self.args = args
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
|
@ -541,6 +542,10 @@ class APIView(View):
|
||||||
return self.response
|
return self.response
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Dispatch checks if the view is async or not and uses the respective
|
||||||
|
async or sync dispatch method.
|
||||||
|
"""
|
||||||
if hasattr(self, 'view_is_async') and self.view_is_async:
|
if hasattr(self, 'view_is_async') and self.view_is_async:
|
||||||
return self.async_dispatch(request, *args, **kwargs)
|
return self.async_dispatch(request, *args, **kwargs)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -45,6 +45,18 @@ def basic_view(request):
|
||||||
return Response({'method': 'PATCH', 'data': request.data})
|
return Response({'method': 'PATCH', 'data': request.data})
|
||||||
|
|
||||||
|
|
||||||
|
@api_view(['GET', 'POST', 'PUT', 'PATCH'])
|
||||||
|
async def basic_async_view(request):
|
||||||
|
if request.method == 'GET':
|
||||||
|
return Response({'method': 'GET'})
|
||||||
|
elif request.method == 'POST':
|
||||||
|
return Response({'method': 'POST', 'data': request.data})
|
||||||
|
elif request.method == 'PUT':
|
||||||
|
return Response({'method': 'PUT', 'data': request.data})
|
||||||
|
elif request.method == 'PATCH':
|
||||||
|
return Response({'method': 'PATCH', 'data': request.data})
|
||||||
|
|
||||||
|
|
||||||
class ErrorView(APIView):
|
class ErrorView(APIView):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
raise Exception
|
raise Exception
|
||||||
|
@ -173,6 +185,40 @@ class ClassBasedAsyncViewIntegrationTests(TestCase):
|
||||||
assert sanitise_json_error(response.data) == expected
|
assert sanitise_json_error(response.data) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
django.VERSION < (4, 1),
|
||||||
|
reason="Async view support requires Django 4.1 or higher",
|
||||||
|
)
|
||||||
|
class FunctionBasedAsyncViewIntegrationTests(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.view = basic_async_view
|
||||||
|
|
||||||
|
def test_get_succeeds(self):
|
||||||
|
request = factory.get('/')
|
||||||
|
response = async_to_sync(self.view)(request)
|
||||||
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
assert response.data == {'method': 'GET'}
|
||||||
|
|
||||||
|
def test_post_succeeds(self):
|
||||||
|
request = factory.post('/', {'test': 'foo'})
|
||||||
|
response = async_to_sync(self.view)(request)
|
||||||
|
expected = {
|
||||||
|
'method': 'POST',
|
||||||
|
'data': {'test': ['foo']}
|
||||||
|
}
|
||||||
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
assert response.data == expected
|
||||||
|
|
||||||
|
def test_400_parse_error(self):
|
||||||
|
request = factory.post('/', 'f00bar', content_type='application/json')
|
||||||
|
response = async_to_sync(self.view)(request)
|
||||||
|
expected = {
|
||||||
|
'detail': JSON_ERROR
|
||||||
|
}
|
||||||
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||||
|
assert sanitise_json_error(response.data) == expected
|
||||||
|
|
||||||
|
|
||||||
class TestCustomExceptionHandler(TestCase):
|
class TestCustomExceptionHandler(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.DEFAULT_HANDLER = api_settings.EXCEPTION_HANDLER
|
self.DEFAULT_HANDLER = api_settings.EXCEPTION_HANDLER
|
||||||
|
|
Loading…
Reference in New Issue
Block a user