Урок курса
Зависимости через Depends
FastAPI для начинающих: API с базой данных и тестамиОбработчики в роутере пока вызывают вспомогательные функции вручную. Depends поручает FastAPI вызов функции и передаёт её результат в параметр обработчика; поиск записи или подготовка параметров остаются в самой зависимости.
Depends: функция и её результат в аргументе обработчика
FastAPI импортируется вместе с Depends из того же пакета:
from fastapi import APIRouter, Depends
Когда FastAPI видит параметр обработчика с Depends(...), он не ждёт, что значение придёт снаружи — из пути или query. Вместо этого FastAPI сам вызывает переданную функцию, берёт то, что она вернула, и подставляет результат в этот параметр.
Простой пример с функцией пагинации:
def get_pagination(skip: int = 0, limit: int = 10) -> dict:
return {"skip": skip, "limit": limit}
@router.get("")
def list_rooms(pagination: dict = Depends(get_pagination)):
rooms = list(rooms_db.values())
return rooms[pagination["skip"]: pagination["skip"] + pagination["limit"]]
Здесь pagination — не query-параметр и не path-параметр. FastAPI вызывает get_pagination, получает словарь {"skip": ..., "limit": ...} и передаёт его в list_rooms как значение pagination. Обработчик вообще не видит skip и limit напрямую.
Функция без скобок. В Depends(get_pagination) передаётся сама функция — объект, который FastAPI сможет вызвать при обработке каждого запроса. Если написать Depends(get_pagination()) со скобками, Python вызовет get_pagination немедленно, в момент загрузки модуля, и передаст в Depends обычный словарь {"skip": 0, "limit": 10}. Словарь — не вызываемый объект, FastAPI не сможет его использовать как зависимость, и при старте приложения маршрут не зарегистрируется. Декоратор выполняется при импорте модуля, поэтому этот импорт тоже прервётся ошибкой регистрации.
Для показанной зависимости передайте get_pagination, а не результат вызова get_pagination(): функция возвращает словарь, который нельзя вызвать как зависимость.
get_pagination: откуда берутся skip и limit
Функция-зависимость — обычная Python-функция, и FastAPI применяет к её параметрам те же правила, что и к параметрам обработчика.
Для get_pagination(skip: int = 0, limit: int = 10) ни skip, ни limit не совпадают ни с одной переменной пути в маршруте GET /api/rooms. Поэтому FastAPI читает их из query-строки. Если параметр отсутствует в запросе, берётся default. Если значение не конвертируется в int, FastAPI возвращает 422 до вызова обработчика — прямо на этапе разбора зависимости.
Для самостоятельного примера пагинации создайте отдельный каталог pagination_demo, а в нём два файла ниже и пустой routers/__init__.py. Это сокращённый пример с тремя готовыми записями: не заменяйте им рабочий CRUD-проект из урока16.
routers/rooms.py
from fastapi import APIRouter, Depends
router = APIRouter()
rooms_db: dict[int, dict] = {
1: {"id": 1, "name": "Альфа", "capacity": 6},
2: {"id": 2, "name": "Бета", "capacity": 12},
3: {"id": 3, "name": "Гамма", "capacity": 4},
}
def get_pagination(skip: int = 0, limit: int = 10) -> dict:
return {"skip": skip, "limit": limit}
@router.get("")
def list_rooms(pagination: dict = Depends(get_pagination)):
rooms = list(rooms_db.values())
return rooms[pagination["skip"]: pagination["skip"] + pagination["limit"]]
main.py
from fastapi import FastAPI
from routers import rooms
app = FastAPI()
app.include_router(rooms.router, prefix="/api/rooms")
Запустите из каталога рядом с main.py:
uvicorn main:app --reload
В запущенном pagination_demo доступны следующие запросы:
GET /api/rooms— нет query, оба defaults вступают в силу, возвращаются все три записи.GET /api/rooms?skip=1&limit=2— FastAPI передаётskip=1,limit=2вget_pagination; та возвращает словарь; срезrooms[1:3]даёт записи сid=2иid=3.GET /api/rooms?skip=abc— строкуabcнельзя привести кint, FastAPI отвечает422; один из элементов спискаdetailв JSON-ответе содержит"loc": ["query", "skip"]. Доlist_roomsвыполнение не доходит.
Все значения по умолчанию и логика сборки словаря сосредоточены в get_pagination. Если завтра появится второй маршрут с пагинацией, он подключит ту же функцию и получит те же параметры без копирования кода.
get_room_or_404 как зависимость: room_id берётся из пути
Вернитесь к рабочему CRUD-проекту из урока16. В его routers/rooms.py уже есть RoomOut, get_room_or_404 и нужные импорты; сокращённый pagination_demo из предыдущего раздела здесь не используется. Добавьте Depends к импорту из fastapi. get_room_or_404 ищет переговорную или выбрасывает HTTPException(404). Прежний обработчик вызывал её вручную:
@router.get("/{room_id}", response_model=RoomOut)
def get_room(room_id: int):
return get_room_or_404(room_id)
Замените прежний GET по id вместе с декоратором на фрагмент ниже; второй маршрут на тот же путь не добавляйте. С Depends вызов выполняет FastAPI:
@router.get("/{room_id}", response_model=RoomOut)
def get_room(room: dict = Depends(get_room_or_404)):
return room
Сигнатура get_room_or_404(room_id: int) не меняется. FastAPI видит параметр room_id в зависимости и смотрит, есть ли в маршруте переменная пути с таким именем. Маршрут /{room_id} — есть. Значит, room_id читается из пути, а не из query.
Это проявляется в интересной ситуации: запрос GET /api/rooms/1?room_id=99 возвращает переговорную с id=1. Значение из query игнорируется, потому что имя совпало с переменной пути. Аннотация int задаёт только преобразование типа — она не меняет источник.
Если room_id=1 есть в rooms_db, зависимость возвращает найденный словарь, и FastAPI подставляет его в room. Обработчик просто возвращает room — отдельный аргумент room_id в нём больше не нужен.
Если room_id=99 нет в rooms_db, get_room_or_404 выбрасывает HTTPException(404). FastAPI перехватывает исключение и отправляет ответ 404 — тело обработчика не выполняется.
Изменился только способ вызова функции: раньше его выполнял обработчик, теперь FastAPI. Логика поиска в rooms_db остаётся той же.

Попробуйте решить
Контекст подключения: код роутера находится в routers/rooms.py; main.py создаёт app = FastAPI() и после импорта from routers import rooms вызывает app.include_router(rooms.router, prefix="/api/rooms"). В декораторах пишите только часть после prefix; путь списка — пустая строка.
Объявлена зависимость:
def get_pagination(skip: int = 0, limit: int = 10) -> dict:
return {"skip": skip, "limit": limit}
И маршрут:
@router.get("")
def list_rooms(pagination: dict = Depends(get_pagination)):
...
Клиент отправляет GET /api/rooms?limit=5. Откуда FastAPI берёт значение skip при выполнении get_pagination?
Продолжить с проверкой и прогрессом
Откройте интерактивный раннер с заданиями урока.
