Starting with Django 3.1, the latest version that dropped a couple of weeks ago, Django now supports fully asynchronous request path. This is exciting for everyone who’s been waiting on the edge of their seats ever since Andrew Godwin’s DEP 0009 was approved by Django Technical Board in July 2019. Read on to know all about what this release means if you have a Django application in production and looking to add async support.
At DeepSource, we’re working on adding more Django issues in our Python analyzer, which will also include async-specific bug risks and anti-patterns that are detected using static analysis.
In Django 3.1, async features are now supported across the request-response cycle. This means you can define fully asynchronous views using the async
keyword:
async def get_cat_facts(request):
"""Return a random cat fact."""
async with aiohttp.ClientSession() as session:
async with session.get('https://catfact.ninja/fact') as resp:
data = await resp.json()
return data
The most exciting thing about this is that async support is entirely backwards-compatible. This means you can use any combination of async and sync views, and Django will guarantee that the execution is always done in the right execution context. So if you want to dip your hands without porting everything right away, you can just flip the switch and add just one async view, without any speed regressions on the existing synchronous code.
You can also use the goodness of async in your middleware functions:
def simple_middleware(get_response):
async def middleware(request):
# do something interesting
response = await get_response(request)
return response
return middleware
Since middleware can support any combination of sync and async requests, Django will try to fit the middleware’s requirements if only one type of requests are supported (like only async requests in the example above) — but at a performance penalty. It is recommended to use the sync_and_async_middleware
decorator to define middleware that support both kinds of requests. More examples can be found in the docs.
All of Django’s async features are fully supported on both WSGI and ASGI — although there will be performance penalties if you run async code with WSGI and long-running requests won’t be efficient. If you want to make your sync code work well with your all-new async views and ASGI mode, use of the sync_to_async decorator is recommended.
If you are doing a lot of external HTTP calls from a view, async views allow you to natively make those calls in parallel. This can provide great speed bumps, especially if you had been using async code inside sync views before. If your views involve heavy-lifting calculations or long-running network calls to be done as part of the request path, it’s a great use case for using async views.
The ORM, cache layer, and several other parts of code that involve long-running network calls do not support async yet. Support for async features in the ORM is expected to come sooner as it is a part of the initial DEP. Features like templating and cache backends will need some more time, as those will need their own separate DEPs and research to be fully async.
Django 3.0 started the journey of bringing full async capability to Django, starting with adding support for ASGI which could run sync code. With this release, you can actually put async code in production if you don’t have to do heavy-lifting with some parts of Django that don’t support async yet. The fact that all changes are guaranteed to be 100% backward compatible is an impressive feat in itself — and it reassures the bright future of full async support in Django soon!