Call coroutines in django views

The Problem

You have coroutine declared with async syntax and you need to call it in Django view. How can you do it ?

For the sake of simplicity, Suppose the coroutine with async syntax return a string. In the Django view, you need to call the coroutine, find the generated string and respond with text file whose content is this value.

It seems straightforward task. You naively try to write the following code:

data.py - module with coroutine declared with async syntax
 import asyncio

 async def msg():
     await asyncio.sleep(2)
     return 'hallo'
urls.py
 from django.urls import path
 from . import views

 urlpatterns = [
     path('hello', views.hello, name='hello')
 ]
views.py
 from . import data

 def hello(request):
     ss = data.hello()
     return HttpResponse(ss,content_type='text/plain')

However when you test the URL you get the following:

text file at /hello
 <coroutine object msg at 0x7f8504696d48>

Well, You remember that a function with async syntax will always return coroutine object. This is not what you want.

You modify the code to use await construct:

views.py
 from . import data

 def hello(request):
     ss = await data.hello()
     return HttpResponse(ss,content_type='text/plain')

However, It does not solve the problem. You get SyntaxError: 'await' outside async function

SyntaxError: 'await' outside async function
 $ ./manage.py runserver 0:8000
 Performing system checks...

 Unhandled exception in thread ...
 ss = await data.msg()
         ^
 SyntaxError: 'await' outside async function

You modify the code and convert the view function to coroutine with the async construct.

views.py
 from . import data

 async def hello(request):
     ss = await data.hello()
     return HttpResponse(ss,content_type='text/plain')

However, It does not solve the problem. An exception is raised when accessing the url:

/hello exception
 AttributeError at /hello
 'coroutine' object has no attribute 'get'
Oops!

As mentioned above - A function with async syntax will always return coroutine object. However, It seems that Django (at least, Django 2.15) expect the return value of the view to be HttpResponse and not a coroutine.

Solution - use asyncio.new_event_loop in the view

Using asyncio.new_event_loop will allow to call the coroutine

views.py
 from . import data

 def hello(request):
     loop = asyncio.new_event_loop()
     ss = loop.run_until_complete( data.msg() )
     loop.close()

     return HttpResponse(ss,content_type='text/plain')