-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Async implementation #8617
Async implementation #8617
Changes from 1 commit
79ce07b
67ebe90
7d2b73a
8fa1b7c
daac20a
0a40010
ae84d97
351ea7d
0400834
5e3140a
472ca5b
7b18380
053eabf
f65e859
f16767f
c6cf2d7
ec1d754
de6df46
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
from django.views.decorators.csrf import csrf_exempt | ||
from django.views.generic import View | ||
|
||
from rest_framework.compat import sync_to_async | ||
from rest_framework import exceptions, status | ||
from rest_framework.request import Request | ||
from rest_framework.response import Response | ||
|
@@ -524,7 +525,7 @@ async def async_dispatch(self, request, *args, **kwargs): | |
self.headers = self.default_response_headers # deprecate? | ||
|
||
try: | ||
self.initial(request, *args, **kwargs) | ||
sync_to_async(self.initial)(request, *args, **kwargs) | ||
em1208 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# Get the appropriate handler method | ||
if request.method.lower() in self.http_method_names: | ||
|
@@ -555,7 +556,16 @@ def options(self, request, *args, **kwargs): | |
""" | ||
Handler method for HTTP 'OPTIONS' request. | ||
""" | ||
if self.metadata_class is None: | ||
return self.http_method_not_allowed(request, *args, **kwargs) | ||
data = self.metadata_class().determine_metadata(request, self) | ||
return Response(data, status=status.HTTP_200_OK) | ||
def func(): | ||
if self.metadata_class is None: | ||
return self.http_method_not_allowed(request, *args, **kwargs) | ||
data = self.metadata_class().determine_metadata(request, self) | ||
return Response(data, status=status.HTTP_200_OK) | ||
|
||
if hasattr(self, 'view_is_async') and self.view_is_async: | ||
em1208 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
async def handler(): | ||
return func() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I checked the code and under some conditions |
||
else: | ||
def handler(): | ||
return func() | ||
return handler() |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
import django | ||
import pytest | ||
from django.test import TestCase | ||
from django.contrib.auth.models import User | ||
|
||
from rest_framework import status | ||
from rest_framework.compat import async_to_sync | ||
|
@@ -101,6 +102,15 @@ def test_get_succeeds(self): | |
assert response.status_code == status.HTTP_200_OK | ||
assert response.data == {'method': 'GET'} | ||
|
||
def test_logged_in_get_succeeds(self): | ||
user = User.objects.create_user('user', '[email protected]', 'password') | ||
request = factory.get('/') | ||
del user.is_active | ||
em1208 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
request.user = user | ||
em1208 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
response = 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 = self.view(request) | ||
|
@@ -111,6 +121,11 @@ def test_post_succeeds(self): | |
assert response.status_code == status.HTTP_200_OK | ||
assert response.data == expected | ||
|
||
def test_options_succeeds(self): | ||
request = factory.options('/') | ||
response = self.view(request) | ||
assert response.status_code == status.HTTP_200_OK | ||
|
||
def test_400_parse_error(self): | ||
request = factory.post('/', 'f00bar', content_type='application/json') | ||
response = self.view(request) | ||
|
@@ -131,6 +146,15 @@ def test_get_succeeds(self): | |
assert response.status_code == status.HTTP_200_OK | ||
assert response.data == {'method': 'GET'} | ||
|
||
def test_logged_in_get_succeeds(self): | ||
user = User.objects.create_user('user', '[email protected]', 'password') | ||
request = factory.get('/') | ||
del user.is_active | ||
request.user = user | ||
response = 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 = self.view(request) | ||
|
@@ -141,6 +165,11 @@ def test_post_succeeds(self): | |
assert response.status_code == status.HTTP_200_OK | ||
assert response.data == expected | ||
|
||
def test_options_succeeds(self): | ||
request = factory.options('/') | ||
response = self.view(request) | ||
assert response.status_code == status.HTTP_200_OK | ||
|
||
def test_400_parse_error(self): | ||
request = factory.post('/', 'f00bar', content_type='application/json') | ||
response = self.view(request) | ||
|
@@ -165,6 +194,15 @@ def test_get_succeeds(self): | |
assert response.status_code == status.HTTP_200_OK | ||
assert response.data == {'method': 'GET'} | ||
|
||
def test_logged_in_get_succeeds(self): | ||
user = User.objects.create_user('user', '[email protected]', 'password') | ||
request = factory.get('/') | ||
del user.is_active | ||
request.user = user | ||
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) | ||
|
@@ -175,6 +213,11 @@ def test_post_succeeds(self): | |
assert response.status_code == status.HTTP_200_OK | ||
assert response.data == expected | ||
|
||
def test_options_succeeds(self): | ||
request = factory.options('/') | ||
response = async_to_sync(self.view)(request) | ||
assert response.status_code == status.HTTP_200_OK | ||
|
||
def test_400_parse_error(self): | ||
request = factory.post('/', 'f00bar', content_type='application/json') | ||
response = async_to_sync(self.view)(request) | ||
|
@@ -199,6 +242,15 @@ def test_get_succeeds(self): | |
assert response.status_code == status.HTTP_200_OK | ||
assert response.data == {'method': 'GET'} | ||
|
||
def test_logged_in_get_succeeds(self): | ||
user = User.objects.create_user('user', '[email protected]', 'password') | ||
request = factory.get('/') | ||
del user.is_active | ||
request.user = user | ||
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) | ||
|
@@ -209,6 +261,11 @@ def test_post_succeeds(self): | |
assert response.status_code == status.HTTP_200_OK | ||
assert response.data == expected | ||
|
||
def test_options_succeeds(self): | ||
request = factory.options('/') | ||
response = async_to_sync(self.view)(request) | ||
assert response.status_code == status.HTTP_200_OK | ||
|
||
def test_400_parse_error(self): | ||
request = factory.post('/', 'f00bar', content_type='application/json') | ||
response = async_to_sync(self.view)(request) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A type checker would not be happy about
async_to_sync = None
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
async_to_sync
is available in Django since version 3.1. In this case I'm using it just to run the test. If pytest-asyncio is added then it can be removed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would personally rather we take a testing dependency on
pytest-asyncio
rather than implement a test-only compat helper.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it's best to defer the decision to add an additional dependency to project leads. Especially if the new dependency can be easily circumvented, such as in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an a user of drf I would not happy to have any async tests related dependency in my project