async views, async ORM, WebSockets, channels, async middleware
Django работает поверх ASGI с версии 3.0, а с 4.1 умеет выполнять асинхронные view, ORM-запросы и middleware. Только async ускоряет не всё подряд. Он помогает там, где процесс большую часть времени ждёт — внешний API, БД, сокет, — и почти ничего не даёт, когда процесс занят вычислениями.
💡 Правило: async выигрывает на I/O-bound нагрузке с высокой конкурентностью. На CPU-bound (обработка изображений, ML-инференс) async не поможет — там нужны процессы/воркеры. Если сомневаетесь — оставайтесь на синхронном Django, он проще и для 90% проектов быстрее в разработке.
Синхронный view удерживает воркер (поток/процесс Gunicorn) на всё время запроса. Пока вы ждёте ответа от платёжного шлюза 800 мс — воркер простаивает, но занят. 10 воркеров = максимум 10 одновременных «висящих» запросов.
Асинхронный view на время await отдаёт управление циклу событий, и тот же процесс обслуживает сотни ожидающих запросов одновременно. Выигрыш появляется только там, где есть это ожидание.
| Сценарий | Async помогает? | Почему |
|---|---|---|
| Агрегация 5 внешних API в одном запросе | ✅ Да | Запросы идут параллельно вместо последовательно |
| Долгий поллинг / SSE / WebSocket | ✅ Да | Тысячи открытых соединений без тысячи потоков |
| Тяжёлый расчёт в Python | ❌ Нет | GIL не отпускается на await, цикл блокируется |
| Обычный CRUD с одним запросом в БД | ⚠️ Скорее нет | Накладные расходы не окупаются |
Async-возможности работают только под ASGI-сервером. python manage.py runserver поднимает ASGI автоматически, в production нужен Uvicorn/Daphne (часто под Gunicorn-воркером):
# Production: Gunicorn управляет Uvicorn-воркерами
gunicorn myproject.asgi:application \
-k uvicorn.workers.UvicornWorker \
--workers 4 --bind 0.0.0.0:8000# myproject/asgi.py — генерируется Django автоматически
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
application = get_asgi_application()View становится асинхронным, если объявлен через async def. Django сам определяет тип и запускает его в нужном контексте.
import httpx
from django.http import JsonResponse
async def dashboard(request):
# Три внешних запроса ИДУТ ПАРАЛЛЕЛЬНО, а не один за другим
import asyncio
async with httpx.AsyncClient() as client:
weather, rates, news = await asyncio.gather(
client.get('https://api.weather.example/now'),
client.get('https://api.rates.example/usd'),
client.get('https://api.news.example/top'),
)
return JsonResponse({
'weather': weather.json(),
'rates': rates.json(),
'news': news.json(),
})⚠️ Главная ловушка: вызов синхронной функции (особенно ORM или
requests) внутриasync defзаблокирует весь цикл событий, и все параллельные запросы встанут. Синхронный код нужно оборачивать вsync_to_async.
У большинства методов QuerySet появились a-двойники: aget, acreate, afirst, acount, aexists, aupdate, adelete. Для итерации — async for.
from asgiref.sync import sync_to_async
async def get_user(user_id):
user = await User.objects.aget(pk=user_id)
return user
async def list_recent_posts():
posts = []
async for post in Post.objects.filter(is_published=True).aiterator():
posts.append(post)
return posts
async def create_comment(post_id, text):
return await Comment.objects.acreate(post_id=post_id, text=text)⚠️ Ленивость никуда не делась. Обращение к связанному объекту (
post.author) внутри async-кода вызывает синхронный запрос и упадёт сSynchronousOnlyOperation. Решение —select_related/prefetch_relatedзаранее, либоawait sync_to_async(lambda: post.author)().
# ❌ Упадёт: ленивый доступ к author внутри async
async def bad(post_id):
post = await Post.objects.aget(pk=post_id)
return post.author.username # SynchronousOnlyOperation!
# ✅ Подгрузили author заранее одним запросом
async def good(post_id):
post = await Post.objects.select_related('author').aget(pk=post_id)
return post.author.usernameРеальные проекты почти всегда смешанные. Связывают два мира два адаптера из asgiref:
| Адаптер | Когда |
|---|---|
sync_to_async(fn) | Вызвать синхронную функцию (ORM, requests, легаси) из async-кода |
async_to_sync(fn) | Вызвать async функцию из синхронного кода (Celery-задача, management-команда) |
from asgiref.sync import sync_to_async
@sync_to_async
def charge_card(user, amount):
# синхронная библиотека платежей
return payment_gateway.charge(user.card_token, amount)
async def checkout(request):
result = await charge_card(request.user, 1000)
...💡 По умолчанию
sync_to_asyncвыполняет код в отдельном потоке (thread_sensitive=Trueдля безопасности с ORM-соединениями). Не оборачивайте им CPU-bound код в надежде на ускорение — потоки в Python не дают параллелизма из-за GIL.
«Чистый» Django не умеет долгоживущие соединения — для WebSocket/SSE нужен пакет Channels, который добавляет слой consumer'ов и channel layer (обычно на Redis) для рассылки сообщений между процессами.
# consumers.py
from channels.generic.websocket import AsyncJsonWebsocketConsumer
class ChatConsumer(AsyncJsonWebsocketConsumer):
async def connect(self):
self.room = self.scope['url_route']['kwargs']['room']
await self.channel_layer.group_add(self.room, self.channel_name)
await self.accept()
async def disconnect(self, code):
await self.channel_layer.group_discard(self.room, self.channel_name)
async def receive_json(self, content):
# Рассылаем сообщение всем в комнате
await self.channel_layer.group_send(
self.room,
{'type': 'chat.message', 'text': content['text']},
)
async def chat_message(self, event):
await self.send_json({'text': event['text']})# routing.py
from django.urls import path
websocket_urlpatterns = [
path('ws/chat/<str:room>/', ChatConsumer.as_asgi()),
]import pytest
@pytest.mark.asyncio
async def test_dashboard(async_client):
response = await async_client.get('/dashboard/')
assert response.status_code == 200Django предоставляет AsyncClient (асинхронный аналог Client) и AsyncRequestFactory для тестов async-view.
async def view только когда внутри есть реальный awaitawait asyncio.gather(...) для параллельных внешних вызововaget, acreate, aiterator) и помните про select_relatedsync_to_async, async — в async_to_syncSynchronousOnlyOperationВопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.
Далее: Фоновые задачи: Celery