Từ Django 3.1, view có thể là async def.
Lợi rõ khi view chờ I/O nhiều — gọi external API, đọc nhiều endpoint song song, WebSocket.
import asyncio
import httpx
from django.http import JsonResponse
async def aggregate(request):
async with httpx.AsyncClient() as client:
a, b = await asyncio.gather(
client.get('https://api.x/users'),
client.get('https://api.y/posts'),
)
return JsonResponse({'users': a.json(), 'posts': b.json()})Rào cản lớn nhất là ORM.
- Django ORM bản chất là sync — gọi
Post.objects.filter(...)trong async view sẽ bịSynchronousOnlyOperation. - Phải dùng async variant:
await Post.objects.aget(...),await Post.objects.acreate(...),async for post in Post.objects.all(), hoặc wrap bằngsync_to_async(...). - Driver Postgres mặc định (
psycopg2) cũng là sync, nên Django chạy ORM trong threadpool chứ không phải event loop thực sự. - Driver async thật sự là
psycopg3(Django 4.2+ support một phần).
async def view(request):
posts = []
async for post in Post.objects.all()[:10]:
posts.append({'id': post.id, 'title': post.title})
return JsonResponse({'posts': posts})Đừng đổi mọi view sang async vì "nghe có vẻ nhanh hơn".
- Sync view dưới Gunicorn
gthreadxử lý 90% case rất ổn. - Async chỉ thắng rõ khi đợi nhiều I/O song song; query DB tuần tự thì sync còn nhanh hơn vì không có overhead event-loop hop.
Since Django 3.1, views can be async def.
The win shows up when the view waits on I/O a lot — external APIs, fan-out reads, WebSocket.
import asyncio
import httpx
from django.http import JsonResponse
async def aggregate(request):
async with httpx.AsyncClient() as client:
a, b = await asyncio.gather(
client.get('https://api.x/users'),
client.get('https://api.y/posts'),
)
return JsonResponse({'users': a.json(), 'posts': b.json()})The biggest pitfall is the ORM:
- The Django ORM is fundamentally sync. Calling Post.objects.filter(...) inside an async view → SynchronousOnlyOperation (Django catches it).
- You must use async variants: await Post.objects.aget(...), await Post.objects.acreate(...), async for post in Post.objects.all(). Or wrap with sync_to_async(...).
- The default Postgres driver (psycopg2) is sync — Django runs ORM in a threadpool, not a real event loop. The truly async driver is psycopg3 (partial support in Django 4.2+).
async def view(request):
posts = []
async for post in Post.objects.all()[:10]:
posts.append({'id': post.id, 'title': post.title})
return JsonResponse({'posts': posts})Pitfall: Do not flip every view to async because "it sounds faster".
- A sync view under Gunicorn
gthreadhandles 90% of cases very well. - Async wins only when waiting on lots of parallel I/O; sequential DB queries are faster sync because there is no event-loop hop overhead.