A Single File Asynchronous Django Application

Didn't Daphne turn into a laurel tree?

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.

Fin

Hope this helps you get started experimenting with Django on ASGI,

—Adam


Newly updated: my book Boost Your Django DX now covers Django 5.0 and Python 3.12.


Subscribe via RSS, Twitter, Mastodon, or email:

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: