30

Since Django now supports async views, I'm trying to change my code base which contains a lot of function based views to be async but for some reason its not working.

@api_view(["GET"])
async def test_async_view(request):
    ...
    data = await get_data()
    return Response(data)

When I send a request to this endpoint, I get an error saying:

AssertionError: Expected a Response, HttpResponse or HttpStreamingResponse to be returned from the view, but received a <class 'coroutine'>

Does DRF not support async views yet? Is there an alternative I can do to get this working?

3 Answers 3

11

As of now, DRF doesn't support async "api views". Here is an open issue (#7260) in the DRF community and it is still in the discussion stage.

But, Django providing a decorator/wrapper which allow us to convert our sync views/function to async using sync_to_async(...) wrapper.

Example,

@sync_to_async
@api_view(["GET"])
def sample_view(request):
    data = get_data()
    return Response(data)

Note that, here, sample_view(...) and get_data(...) are sync functions.

Sign up to request clarification or add additional context in comments.

4 Comments

How would that help? If get_data is sync?
as I mentioned, DRF doesn't support "async API views*. The least workaround is to use the sync_to_async(...). That means the sample_view(...) must be a sync function and thus get_data(...) should also be a sync function.
I am also confused by this. If get_data() is the bulk of the work and must be sync, then what can @sync_to_async possibly achieve?
You can call it with thread safety turned off, and it will run it in a separate thread. Otherwise it will simply wrap the sync code in a wrapper context that allows the async generator to consume it, however there will be a performance loss as it will run in the same thread as the async loop and effectively block other async contexts from executing.
9

You can do it with adrf:

pip install adrf

then add it to INSTALLED_APPS

INSTALLED_APPS = [
    ...
    'adrf',
]

import asyncio
from asgiref.sync import sync_to_async

@sync_to_async  
def do_a_network_call(some_input):  
    expensive_result = do_expensive_network_call(some_input)
    return expensive_result
    
# Class Based Views:
from adrf.views import APIView

class AsyncView(APIView):
    async def get(self, request):
        result = await asyncio.gather(do_a_network_call("some_input"))  
        return Response({"result": result})


# Function Based Views:
from adrf.decorators import api_view

@api_view(['GET'])
async def async_view(request):
    result = await asyncio.gather(do_a_network_call("some_input"))
    return Response({"result": result})

1 Comment

currently it doesn't support many drf methods
0

I think you can Use this decorator in DRF

import asyncio
from functools import wraps


def to_async(blocking):
    @wraps(blocking)
    def run_wrapper(*args, **kwargs):
        return asyncio.run(blocking(*args, **kwargs))

    return run_wrapper

Example of usage

@to_async
@api_view(["GET"])
async def sample_view(request):
    ...

1 Comment

Do not use asyncio.run with Django, as it does allow asgiref.sync.sync_to_async functions to work as intended(which need to be wrapped around Database calls) Please use asgiref.sync.async_to_sync instead

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.