This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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
Discussion: Async view support #7260
Comments
I'm dropping those comments not because they're problematic, but because I think they're derailing the issue somewhat. I don't really want to have a conversation here about the relative merits of opting for an async-native framework. There's great options there and growing support for a wider async ecosystem which encode is also playing it's part in developing. Django is starting to gain some built-in async support. That's useful in some limited cases, and it'd be worthwhile for REST framework to track Django's async support as it grows, even if that comes with some significant caveats at this point in time. |
Btw. maybe https://github.com/hishnash/djangochannelsrestframework can be an simple inspiration |
Would ❤️ some kind of native, official websocket story for DRF that allows reuse of existing DRF components. This would help position DRF as an option for more real-time bi-directional use cases. |
This one is not strictly for views, but the async view would be a precondition for this to be possible. I've been wanting for some time to be able to use async in my serializers, to speed it up when I have highly IO bound calls in my custom serializer fields. Example
For this simple example we would not gain a lot, but some times there are many such custom fields, or even some times I am serializing many cars in a nested serializer. In these cases I imagine there would be a lot to gain. |
Here's a few more use cases that gain a benefit from varying degrees of async support:
|
Expressing my interest to see this happen! I have seen the benchmark for fastapi and DRF, and fastapi is faster. Would love to stick to DRF as I am comfortable with Django and don't have a lot of time to learn fastapi to quickly deliver a good performing API. |
I am also very interested in async support in Django and DRF. Our use case is pretty common I believe. We make IO network calls within our Django models or serializers to an external API. We try to perform most of those calls in Celery but given that those calls are not computationally intensive, that would be simpler to keep them within Django itself. |
@hadim You should be able to do this already with a Django 3.1 view. Use httpx to make the network calls. You don't even need to use ASGI. Just define an async def view and run under WSGI as normal, and Django takes care of the rest. This seems to be the core use-case. It's not clear what DRF needs to add? 🤔 |
@carltongibson I'm not sure |
This comment has been minimized.
This comment has been minimized.
@TheBlusky ah, yes ViewSet... fair point. |
Is there any work on this subject ? Is there any way to help on this matter ? |
@TheBlusky you could experiment with what's required to get an APIView working here. That's the first step I think. |
We have a use case of downloading a file from a cloud storage provider and then passing it on to the client, A good starting point would be the
|
My use case for this is a flow whereby RPC calls are made from my Django Server to RabbitMQ and all the way back to Django and the response is sent to the client who made the Django HTTP call. These RPC messages could take some time to complete and we ideally don't want our application to be blocking while waiting for the RPC reply to come into Django. We would love to use the power of DRF alongside non-blocking async capable API endpoints and all the community-packages that work on top of DRF, such as DRF Access Policy. DRF Access Policy would allow us to control access permissions while maintaining our application as async. |
To be clear: right now ( Had I understood this correctly? |
Only thoughts at this point. |
Any status on this? Django3.1 and async is still awesome for non-ORM views and the |
+1 for async support in DRF. Should be of highest priority! |
+1 |
Could we please have some comments that adds something to the discussion instead of spams ? |
Ok. There is an specific ":+1:" button on Github UI if somebody wants to +1. Somebody could had missed that and this does not mean a spam. That said, I suggest new comments with "+1" to be removed and we can chill from now on. Back to the main question. I tried to @async_to_sync on the view I wanted async, then dug a bit before giving up and getting back to my main card on the company. Is acceptable to just allow the CBV methods to be I think this is better than forcing the methods to be sync and handle this on their own, as on future I expect the async methods to be more common than the sync ones. |
Seeing that Django's own CBV don't have anything async, I still fail to see what's needed on DRF at the moment. |
Once there's support for CBVs in Django's master branch I'd be happy to take a look at this on our side. |
Here is my naive implementation of async CBVs in DRF, I hope it can help. from rest_framework.response import Response
from rest_framework import status
from asgiref.sync import sync_to_async
import asyncio as aio
class AsyncMixin:
"""Provides async view compatible support for DRF Views and ViewSets.
This must be the first inherited class.
class MyViewSet(AsyncMixin, GenericViewSet):
pass
"""
@classmethod
def as_view(cls, *args, **initkwargs):
"""Make Django process the view as an async view.
"""
view = super().as_view(*args, **initkwargs)
async def async_view(*args, **kwargs):
# wait for the `dispatch` method
return await view(*args, **kwargs)
async_view.csrf_exempt = True
return async_view
async def dispatch(self, request, *args, **kwargs):
"""Add async support.
"""
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers
try:
await sync_to_async(self.initial)(
request, *args, **kwargs) # MODIFIED HERE
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# accept both async and sync handlers
# built-in handlers are sync handlers
if not aio.iscoroutinefunction(handler): # MODIFIED HERE
handler = sync_to_async(handler) # MODIFIED HERE
response = await handler(request, *args, **kwargs) # MODIFIED HERE
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(
request, response, *args, **kwargs)
return self.response
class AsyncCreateModelMixin:
"""Make `create()` and `perform_create()` overridable.
Without inheriting this class, the event loop can't be used in these two methods when override them.
This must be inherited before `CreateModelMixin`.
class MyViewSet(AsyncMixin, GenericViewSet, AsyncCreateModelMixin, CreateModelMixin):
pass
"""
async def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
await sync_to_async(serializer.is_valid)(
raise_exception=True) # MODIFIED HERE
await self.perform_create(serializer) # MODIFIED HERE
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
async def perform_create(self, serializer):
await sync_to_async(serializer.save)()
class AsyncDestroyModelMixin:
"""Make `destroy()` and `perform_destroy()` overridable.
Without inheriting this class, the event loop can't be used in these two methods when override them.
This must be inherited before `DestroyModelMixin`.
class MyViewSet(AsyncMixin, GenericViewSet, AsyncDestroyModelMixin, DestroyModelMixin):
pass
"""
async def destroy(self, request, *args, **kwargs):
instance = await sync_to_async(self.get_object)() # MODIFIED HERE
await self.perform_destroy(instance) # MODIFIED HERE
return Response(status=status.HTTP_204_NO_CONTENT)
async def perform_destroy(self, instance):
await sync_to_async(instance.delete)() # MODIFIED HERE
# other mixins can be created similarly |
is DRF supporting async views with async ORM Calls, yet ? |
No. DRF is a Django app, we can only wait for Django to support that. |
I also had to add |
Hey there! 👋🏻 Just chiming in to check whether there have been any progress on supporting Django 3.1's async views so far? Here's our use case: We want to achieve something similar to the following script, but during an API call. The real commands were replaced by a import asyncio, asyncssh, time, os
USERNAME = os.getenv("POC_SSH_USERNAME")
PASSWORD = os.getenv("POC_SSH_PASSWORD")
HOST = os.getenv("POC_SSH_HOST")
async def main():
start = time.time()
folders = [".", "subfolder1", "subfolder2", "subfolder3"]
tasks = []
for folder in folders:
tasks.append(task(folder))
res = await asyncio.gather(*tasks)
print(res)
end = time.time()
print(f"time: {end - start}")
async def task(folder):
async with asyncssh.connect(HOST, username=USERNAME, password=PASSWORD) as conn:
res = await conn.run(f"sleep 5; ls {folder}", check=True)
return res
if __name__ == "__main__":
asyncio.run(main()) We also have some ORM calls to make in the view before and after running the SSH commands. We're currently using a |
Hey, our use case would be: Stack: Django, DRF, ReactJS We are providing corporate compliance related meetings with presence tracking and real time polls. Hence It would be fantastic if using DRF we could enable a meeting moderator to step through decisions/agenda items just by publishing them one after the other. the published item would pop up through REST on meeting participants browser and collect their vote. instantly moderator would see the results come in till all people have voted. with sync this would mean A LOT of frequent check calls, still with some seconds of delay probably. |
@patroqueeet your use case makes me think of websockets, I don’t see how implementing async views in DRF would help 🤔 |
yeah, new area to me. reading all day already docu/tutorials of web socket/async views already... as I don't have web sockets in place yet but giant DRF code, I would have liked to use what we have and not grow the ecosystem of libs further... |
@patroqueeet My solution for websockets + django is next:
Solution proven by time and good load. Actually one process of Aiohttp may handle thousands of websocket connections. |
@Skorpyon checked Aiohttp... looking very nice. but after reading ~20 articles about django/websocket/async and learning about http, starlet, unicorn, Daphne et al yesterday all day long. I will now start doing a quick prototyping based on 3.1 from this guy here https://alex-oleshkevich.medium.com/websockets-in-django-3-1-73de70c5c1ba (following the recommendation from @Crocmagnon) - I like the simplicity and I can use as much of my stack as possible. probably using DRF serializers to render and parse the JSON... Let's see how fast I can fail (making me learn even faster) :) |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Given Django 3.1's upcoming support for async views, it's worth us talking though if there are useful points of async support in REST framework, and what they would be if so.
I'm going to prefix this by starting with a bit of expectation setting... Django 3.1's async view support is a really impressive bit of foundational work, but there's currently limitations to where it's actually valuable, given that the ORM isn't yet async-capable.
One thing that'd be really helpful to this discussion would be concrete use-cases where folks demonstrate an actual use-case where they've used or would use an async view in Django, together with the motivation, and demonstrable improvements vs. sticking with a regular sync view.
We'd also want to scoping this down to the most minimal possible starting point.
In particular, what would we need to change in order to support this?...
The
@api_view
decorator is implemented on top of theAPIView
class based view, so an even more minimal question is: what would we need to change in order to support something like this?...There's a whole bunch of different things to consider there, eg...
But let's just put those aside for the moment.
Django's upcoming docs for 3.1 async views mentions...
So here's some even simpler questions:
Request
explicitly within a Django async view look like (rather than wrapping with @api_view). What provisos are there on operations on the request instance that are currently sync?Response
explicitly within a Django async view look like (rather than wrapping with @api_view). Are there any provisos on sync operations there?The text was updated successfully, but these errors were encountered: