Skip to main content

Django ASGI

Introduction

Django has traditionally used WSGI (Web Server Gateway Interface) for deployment, which works well for synchronous request-response patterns. However, modern web applications often require real-time features like WebSockets, server-sent events, and HTTP/2 support. This is where ASGI (Asynchronous Server Gateway Interface) comes in.

ASGI extends WSGI's capabilities to handle asynchronous code execution and multiple protocols. Starting from Django 3.0, Django provides built-in ASGI support, allowing developers to leverage the power of asynchronous programming for their applications.

In this tutorial, you'll learn:

  • What ASGI is and why it matters
  • How Django supports ASGI
  • Setting up Django with ASGI
  • Deploying Django applications using ASGI servers
  • Real-world examples of ASGI in action

What is ASGI?

ASGI stands for Asynchronous Server Gateway Interface. It's a specification that defines how web servers interact with Python web applications, similar to WSGI but with support for asynchronous code and multiple protocols.

Key differences between WSGI and ASGI

FeatureWSGIASGI
ArchitectureSynchronousAsynchronous
Protocol SupportHTTP onlyHTTP, WebSockets, HTTP/2
Async SupportNoYes
Request HandlingOne request at a timeMultiple concurrent requests

Django's ASGI Support

Since Django 3.0, the framework has included native ASGI support. This means you can run Django applications on ASGI servers and take advantage of asynchronous views, middleware, and other async features.

ASGI Application Configuration

Django provides an ASGI application object through the asgi.py file in your project. When you create a new Django project using django-admin startproject, this file is automatically generated:

python
# myproject/asgi.py
import os

from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')

application = get_asgi_application()

This file creates an ASGI application object that serves as the entry point for ASGI servers.

Setting Up Django with ASGI

Step 1: Create a Django Project

If you don't already have a Django project, create one:

bash
pip install django
django-admin startproject myproject
cd myproject

Step 2: Install an ASGI Server

You'll need an ASGI server to run your Django application. Popular choices include Uvicorn and Daphne. Let's install Uvicorn:

bash
pip install uvicorn

Step 3: Run Your Django Project with ASGI

Now you can run your Django application using Uvicorn:

bash
uvicorn myproject.asgi:application

You should see output similar to:

INFO:     Started server process [28967]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

Your Django application is now running with ASGI!

Asynchronous Views in Django

Django 3.1+ allows you to write asynchronous views using the async def syntax:

python
# views.py
import asyncio
from django.http import HttpResponse

async def async_view(request):
# Simulate an asynchronous operation
await asyncio.sleep(1)
return HttpResponse("Hello from an async view!")

# Regular synchronous view for comparison
def sync_view(request):
import time
time.sleep(1)
return HttpResponse("Hello from a sync view!")

Add these views to your URL patterns:

python
# urls.py
from django.urls import path
from . import views

urlpatterns = [
path('async/', views.async_view),
path('sync/', views.sync_view),
]

When you visit these endpoints, the async view can handle other requests while waiting for the asyncio.sleep() to complete, while the sync view blocks the entire thread.

Using ASGI Middleware

ASGI middleware can enhance your application with features like WebSocket support. Here's how to integrate WebSockets using Channels, a Django project that extends Django's abilities with WebSockets:

Step 1: Install Django Channels

bash
pip install channels

Step 2: Configure Your Project for Channels

Add Channels to your installed apps and configure the ASGI application:

python
# settings.py
INSTALLED_APPS = [
# ...
'channels',
# ...
]

ASGI_APPLICATION = 'myproject.asgi.application'

Step 3: Update Your ASGI Configuration

python
# myproject/asgi.py
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
import app.routing # We'll create this next

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')

application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
app.routing.websocket_urlpatterns
)
),
})

Step 4: Define WebSocket Consumers and Routing

python
# app/consumers.py
from channels.generic.websocket import AsyncWebsocketConsumer
import json

class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
await self.accept()

async def disconnect(self, close_code):
pass

async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']

await self.send(text_data=json.dumps({
'message': f"You said: {message}"
}))
python
# app/routing.py
from django.urls import re_path
from . import consumers

websocket_urlpatterns = [
re_path(r'ws/chat/$', consumers.ChatConsumer.as_asgi()),
]

Real-World Examples

Example 1: Real-time Chat Application

A chat application is a perfect use case for ASGI since it requires real-time communication via WebSockets:

python
# chat/consumers.py
from channels.generic.websocket import AsyncWebsocketConsumer
import json

class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = f'chat_{self.room_name}'

# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)

await self.accept()

async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)

# Receive message from WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']

# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message
}
)

# Receive message from room group
async def chat_message(self, event):
message = event['message']

# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message
}))

Example 2: Integrating Django with an Async API

This example shows how to make asynchronous HTTP requests from a Django view:

python
# views.py
import httpx
from django.http import JsonResponse

async def fetch_data_view(request):
async with httpx.AsyncClient() as client:
# Make multiple concurrent API requests
response1 = client.get('https://api.example.com/data1')
response2 = client.get('https://api.example.com/data2')

# Await responses
results = await asyncio.gather(response1, response2)

# Process results
data1 = results[0].json()
data2 = results[1].json()

# Combine and return
return JsonResponse({
'data1': data1,
'data2': data2
})

Deploying Django ASGI Applications

Using Gunicorn with Uvicorn Workers

For production environments, you can use Gunicorn with Uvicorn workers:

bash
pip install gunicorn uvicorn

Run your application with:

bash
gunicorn myproject.asgi:application -k uvicorn.workers.UvicornWorker

Docker Deployment Example

Here's a sample Dockerfile for a Django ASGI application:

dockerfile
FROM python:3.9

WORKDIR /app

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

CMD ["gunicorn", "myproject.asgi:application", "-k", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:8000"]

Common Issues and Solutions

1. ASGI/WSGI Compatibility

Problem: Some Django features or third-party apps might not be fully compatible with ASGI.

Solution: Django will automatically adapt your synchronous code to run in an asynchronous environment, but it's better to use async-compatible libraries when possible.

2. Database Access

Problem: Django's ORM operations are synchronous and can block the event loop.

Solution: Use sync_to_async from asgiref.sync for database operations in async views:

python
from asgiref.sync import sync_to_async
from django.http import HttpResponse
from myapp.models import MyModel

async def my_async_view(request):
# Convert the synchronous database query to asynchronous
data = await sync_to_async(MyModel.objects.get)(id=1)

# Process data
return HttpResponse(f"Data: {data.name}")

Summary

Django's ASGI support opens a new world of possibilities for building modern web applications:

  • ASGI allows Django to handle asynchronous requests and WebSockets
  • You can write asynchronous views using async def
  • Django Channels provides powerful WebSocket capabilities
  • ASGI servers like Uvicorn and Daphne can be used to run Django applications
  • In production, you can deploy Django ASGI applications using Gunicorn with Uvicorn workers

By embracing ASGI, Django developers can build real-time applications and take advantage of modern web protocols while maintaining Django's powerful features and ease of use.

Additional Resources

Exercises

  1. Create a simple Django application that uses an asynchronous view to fetch data from an external API.
  2. Build a real-time chat application using Django Channels.
  3. Compare the performance of synchronous and asynchronous views in Django.
  4. Deploy a Django ASGI application to a cloud platform of your choice.
  5. Create a middleware that logs request processing times for both synchronous and asynchronous views.

Happy coding with Django ASGI!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)