Урок 12 из 12 · 2 минуты чтения

Веб на Python — WSGI и ASGI, FastAPI, HTTP-клиенты, JSON, Django ORM

Чем WSGI отличается от ASGI, простой API на FastAPI и внедрение зависимостей, requests и httpx с таймаутами, JSON с датами и деньгами, N+1 в Django.

WSGI и ASGI

WSGI — синхронный интерфейс между веб-сервером и приложением: запрос занимает поток или процесс до ответа. На нём Flask и классический Django, серверы — Gunicorn, uWSGI.

ASGI — асинхронный: приложение — корутина, один процесс обслуживает тысячи соединений через цикл событий, поддерживаются WebSocket и долгие соединения. На нём FastAPI, Starlette, Django в режиме ASGI, серверы — Uvicorn, Hypercorn, Granian.

ASGI не ускоряет вычисления и бесполезен с блокирующими библиотеками. Типичный запуск — Gunicorn с воркерами Uvicorn: процессы по числу ядер, внутри каждого — свой цикл событий.

FastAPI

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

class ItemIn(BaseModel):
    name: str
    price: float

@app.get("/items/{item_id}")
async def get_item(item_id: int, full: bool = False):
    item = await repo.get(item_id)
    if item is None:
        raise HTTPException(status_code=404)
    return item

@app.post("/items", status_code=201)
async def create_item(item: ItemIn):
    return await repo.create(item)

Параметр из пути становится аргументом, простые аргументы — query- параметрами, Pydantic-модель — телом запроса. Типы проверяются автоматически, ошибка — 422. Документация API — на /docs, её строят из аннотаций. response_model задаёт схему ответа и скрывает лишние поля.

Внедрение зависимостей

from typing import Annotated
from fastapi import Depends

async def get_session():
    async with SessionLocal() as session:
        yield session              # очистка — после ответа

Session = Annotated[AsyncSession, Depends(get_session)]

@app.get("/orders")
async def list_orders(session: Session): ...

FastAPI вызывает зависимость перед обработчиком и передаёт результат. Зависимости образуют граф, результат кэшируется в пределах запроса, а код после yield выполняется после ответа. В тестах их подменяют через app.dependency_overrides. Идея та же, что у фикстур pytest.

HTTP-клиенты

requests — синхронный, httpx — похожий интерфейс плюс асинхронный клиент и HTTP/2. Обязательные привычки:

with httpx.Client(timeout=5) as client:      # всегда таймаут
    resp = client.get(url)
    resp.raise_for_status()                  # 4xx и 5xx — исключение
    data = resp.json()
  • таймаут всегда — requests по умолчанию ждёт бесконечно и может повесить воркер;
  • клиент или сессия на много запросов — переиспользуются соединения;
  • повторы — только для безопасных запросов и временных ошибок;
  • в асинхронном коде requests блокирует цикл — нужен httpx.AsyncClient.

JSON

json.dumps и json.loads знают только dict, list, str, int, float, bool и None. Кортеж станет списком, ключи — строками. Даты, Decimal, UUID и свои классы нужно преобразовать — параметром default или через Pydantic:

json.dumps(data, default=str, ensure_ascii=False)

Даты передают в ISO 8601 (dt.isoformat()), деньги — строкой или целыми копейками, а не float. ensure_ascii=False оставляет кириллицу читаемой.

N+1 в Django

for order in Order.objects.all():
    print(order.customer.name)     # запрос на каждый заказ

Один запрос за списком и по запросу на каждый элемент. Лечение:

  • select_related("customer") — JOIN в том же запросе для ForeignKey и OneToOne;
  • prefetch_related("items") — отдельный запрос и склейка в Python для ManyToMany и обратных связей.

Найти N+1 помогают django-debug-toolbar, лог SQL и assertNumQueries в тестах. Агрегаты считают в базе — annotate(Count(...)), а не len() в цикле.

Что запомнить

  • WSGI — поток на запрос, ASGI — цикл событий и WebSocket.
  • FastAPI проверяет типы сам и строит документацию из аннотаций.
  • HTTP-запрос — всегда с таймаутом и проверкой статуса.
  • Даты в JSON — ISO 8601, деньги — не float.
  • N+1 лечат select_related и prefetch_related.

Проверка урока

Войдите, чтобы проверить себя: сданные проверки всех уроков курса отмечают его темы пройденными на карте и в ИПР.

Вопросы по теме

Читайте также

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования