A Single File Asynchronous Django Application
Django 3.0 alpha 1 came out this week. It introduces ASGI support thanks to lots of hard work by Andrew Godwin.
Thanks to a question from Emiliano Dalla Verde Marcozzi on the shiny new Django forums, I decided to play with it a bit. I adapted my “single file Django app” to ASGI.
Here’s how to try it yourself.
First, create a new virtual environment (I’m using Python 3.7.4) and install requirements:
$ python -m venv venv
$ source venv/bin/activate
$ pip install django==3.0a1 daphne==2.3.0
Then create a new file app.py
:
import html
import os
import sys
from django.conf import settings
from django.core.asgi import get_asgi_application
from django.http import HttpResponse
from django.urls import path
from django.utils.crypto import get_random_string
settings.configure(
DEBUG=(os.environ.get("DEBUG", "") == "1"),
# Disable host header validation
ALLOWED_HOSTS=["*"],
# Make this module the urlconf
ROOT_URLCONF=__name__,
# We aren't using any security features but Django requires this setting
SECRET_KEY=get_random_string(50),
)
def index(request):
name = request.GET.get("name", "World")
return HttpResponse(f"Hello, {html.escape(name)}!")
urlpatterns = [path("", index)]
application = get_asgi_application()
if __name__ == "__main__":
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
Then run it under Daphne:
$ daphne app:application
Visit http://localhost:8000/?name=Django%20user
in your browser to see “Hello, Django user!”
Async support in Django 3.0 is the first step, and limited to the outer handler layer. Middleware, views, the ORM, and everything you’re used to in Django remains synchronous. The ASGI handler achieves this by running response generation in a thread pool.
If you want to use Websockets, you’ll need a second framework for now, like Channels or Starlette.
ASGI is a simple interface with a “turtles all the way down” approach. This lets us glue our Django app to another with a “middleware” application:
django_application = get_asgi_application()
websocket_application = ... # TODO: make it
async def application(scope, receive, send):
if scope["type"] == "http":
await django_application(scope, receive, send)
elif scope["type"] == "websocket":
await websocket_application(scope, receive, send)
else:
raise NotImplementedError(f"Unknown scope type {scope['type']}")
ASGI support will increase with coming Django versions. DEP 9 outlines Andrew’s plan for increasing ASGI support going forwards.
Newly updated: my book Boost Your Django DX now covers Django 5.0 and Python 3.12.
One summary email a week, no spam, I pinky promise.
Related posts:
- Django versus Flask with Single File Applications
- The Simplest WSGI Middleware
- Where to Learn Django in 2019
Tags: django