Từ Django 3.1, middleware có thể chạy async.
Khai báo bằng cách set async_capable = True + sync_capable = False rồi dùng async def __call__.
class AsyncTimingMiddleware:
sync_capable = False
async_capable = True
def __init__(self, get_response):
self.get_response = get_response
async def __call__(self, request):
start = time.perf_counter()
response = await self.get_response(request)
response['X-Render-MS'] = f'{(time.perf_counter() - start) * 1000:.1f}'
return responseDjango tự chọn mode (sync/async) dựa trên chuỗi middleware + view. Nếu trong chuỗi có một mắt xích chỉ sync, Django wrap async ↔ sync bằng asgiref.sync.async_to_sync / sync_to_async — mỗi lần wrap là một event-loop hop, tức là overhead thực.
Hai lỗi hay gặp: gọi ORM (Post.objects.filter(...)) sync-style trong async middleware sẽ bị SynchronousOnlyOperation, phải dùng await Post.objects.afilter(...) hoặc bọc sync_to_async(...). Và để một sync middleware nằm giữa stack async là toàn pipeline bị degrade vì wrap qua wrap lại.
Đo trước khi đua theo async. Nếu view chính vẫn là sync (Postgres chưa có driver async thực sự ổn), async middleware không cứu được hiệu năng — tối ưu DB hoặc cache thường có ROI cao hơn nhiều.
Since Django 3.1, middleware can be async-capable.
Declare both with the async/sync flags, or simply: set async_capable = True + sync_capable = False in __init__, then use async def __call__.
class AsyncTimingMiddleware:
sync_capable = False
async_capable = True
def __init__(self, get_response):
self.get_response = get_response
async def __call__(self, request):
start = time.perf_counter()
response = await self.get_response(request)
response['X-Render-MS'] = f'{(time.perf_counter() - start) * 1000:.1f}'
return responseDjango picks the mode (sync/async) based on the middleware + view chain. If a single link is sync-only, Django wraps async ↔ sync with asgiref.sync.async_to_sync / sync_to_async → each wrap is an event-loop hop = overhead.
Two common mistakes:
- Calling ORM (Post.objects.filter(...)) in sync-style inside async middleware → SynchronousOnlyOperation. Use await Post.objects.afilter(...) or wrap with sync_to_async(...).
- One sync middleware sandwiched in the async stack → the entire pipeline degrades from back-and-forth wrapping.
Pitfall: Measure before chasing async. If the main view is sync (no production-grade async driver for Postgres), async middleware will not save you; tuning DB / cache usually has higher ROI.