Skip to content

Глава 3.3: Модели данных с Pydantic

Время изучения: 50 минут


1. Pydantic: "Цифровой чертеж" космического корабля

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

  • Название корабля (тип: строка, макс. длина: 50 символов)
  • Год запуска (тип: целое число)
  • Наличие гипердвигателя (тип: да/нет)

Pydantic — это библиотека, которая позволяет создавать такие "цифровые чертежи" для ваших данных в Python. В FastAPI она выполняет три ключевые функции:

  1. Декларация структуры: Четко описывает, из каких полей состоят ваши данные.
  2. Валидация данных: Автоматически проверяет, соответствуют ли входящие данные чертежу.
  3. Документация: FastAPI использует эти чертежи для создания подробной и интерактивной документации.

💡 Космическая аналогия: Модель Pydantic — это технический паспорт объекта. Любой "груз" (данные), прибывающий на станцию, должен соответствовать спецификации в паспорте. Если нет — бортовой компьютер (Pydantic) его отклонит.


2. Создание первого чертежа: Модель Spaceship

Давайте создадим модель, которая будет описывать наш космический аппарат.

Шаг 1: Импортируем BaseModel из Pydantic Pydantic уже установлен вместе с fastapi[all]. Нам нужно только импортировать базовый класс для наших моделей.

Добавьте в main.py в самом верху, рядом с другими импортами:

# main.py
from fastapi import FastAPI
from pydantic import BaseModel

Шаг 2: Описываем модель Spaceship Создайте класс, который наследуется от BaseModel. Внутри класса определите поля и их типы, используя стандартные подсказки типов Python.

Добавьте этот код в main.py (можно после импортов):

class Spaceship(BaseModel):
    """
    Технический паспорт (модель) космического корабля.
    """
    name: str
    type: str
    launch_year: int
    status: str
Вот и всё! Вы только что создали "чертеж". Pydantic теперь знает, что у любого объекта типа Spaceship должны быть четыре поля с указанными типами.


3. Применение модели: Улучшаем наши endpoints

Теперь давайте используем нашу новую модель, чтобы сделать API "умнее".

A. Модель как ответ (Response Model) Мы можем указать FastAPI, что наш endpoint должен возвращать данные, соответствующие модели Spaceship. Это гарантирует, что ответ всегда будет иметь правильную структуру.

Измените endpoint /spaceships/{ship_id} следующим образом:

# main.py

# ... код с db_spaceships и моделью Spaceship ...

# Используем `response_model` для указания "чертежа" ответа
@app.get("/spaceships/{ship_id}", response_model=Spaceship)
def get_spaceship(ship_id: int):
    """
    Возвращает данные о корабле, соответствующие модели Spaceship.
    """
    ship = db_spaceships.get(ship_id)
    return ship
- response_model=Spaceship: Мы говорим FastAPI: "Ответ этой функции должен соответствовать структуре Spaceship. Отфильтруй все лишние поля и убедись, что типы верны".

Что это дает?

  • Фильтрация данных: Если бы в db_spaceships были лишние поля (например, "secret_code"), они бы не попали в финальный JSON.
  • Гарантия структуры: Клиент API может быть уверен, что всегда получит ответ в ожидаемом формате.
  • Документация: В /docs теперь будет показан точный пример ответа (Example Value).

B. Модели для коллекций А что с эндпоинтом /spaceships, который возвращает список кораблей? Для этого нужно использовать list из модуля typing.

Измените импорты и endpoint /spaceships:

# main.py вверху
from fastapi import FastAPI
from pydantic import BaseModel
from typing import List  # <-- Импортируем List

# ... код ...

# Указываем, что ответ - это список (List) объектов типа Spaceship
@app.get("/spaceships", response_model=List[Spaceship])
def get_spaceships():
    """
    Возвращает список кораблей. Каждый элемент списка
    проверяется по модели Spaceship.
    """
    # Pydantic не может работать со словарем, ключами которого являются ID.
    # Преобразуем наш словарь в простой список.
    return list(db_spaceships.values())

  • response_model=List[Spaceship]: Мы указываем, что ответом будет список, где каждый элемент — это объект, соответствующий модели Spaceship.
  • return list(db_spaceships.values()): Важное изменение! Pydantic ожидает итерируемый объект (список), а не словарь, где ключи — это ID. Мы преобразуем значения нашего "симулятора БД" в список.

4. Проверка улучшенного API

Убедитесь, что сервер uvicorn запущен с --reload.

  1. Проверьте http://127.0.0.1:8000/spaceships: Теперь ответ — это JSON-массив, а не объект. Это более правильная и стандартная структура для коллекций.
    [
      { "name": "Voyager-1", "type": "Зонд", ... },
      { "name": "Hubble Space Telescope", ... }
    ]
    
  2. Проверьте http://127.0.0.1:8000/spaceships/1: Ответ не изменился, но теперь он гарантированно соответствует модели.
  3. Загляните в /docs: В разделе "Schemas" внизу страницы появилась ваша модель Spaceship. А в примерах ответов для эндпоинтов теперь отображается красивая, структурированная схема данных.

5. Продвинутая валидация: "Бортовой компьютер" в действии

Pydantic может делать гораздо больше, чем просто проверять типы.

Добавим валидацию в нашу модель Spaceship:

from pydantic import BaseModel, Field

class Spaceship(BaseModel):
    name: str = Field(..., min_length=3, max_length=50, description="Название корабля")
    type: str
    launch_year: int = Field(..., gt=1950, description="Год запуска должен быть после 1950")
    status: str

  • Field(...): Используется для добавления дополнительных правил валидации.
  • ... (Ellipsis): Означает, что поле является обязательным.
  • min_length, max_length: Ограничения для строки.
  • gt: "Greater Than" (больше чем).

Хотя мы еще не создаем новые корабли (это будет в следующей главе), эти правила уже будут отражены в документации и сработают, когда мы реализуем POST-запросы.


Квиз для закрепления

1. Pydantic в FastAPI используется для...

2. Чтобы создать модель данных, нужно унаследовать класс от...

3. Параметр `response_model` в декораторе `@app.get` нужен чтобы...

4. Как указать, что endpoint возвращает *список* объектов типа `Item`?

5. `Field(..., gt=0)` в модели Pydantic означает, что поле...


🚀 Итог главы:

Вы спроектировали "цифровые чертежи" для данных вашего API. Теперь оно не просто работает, а работает предсказуемо и надежно.

  • 📝 Создана модель Spaceship с помощью Pydantic.
  • 🛡️ API теперь валидирует и фильтрует исходящие данные с помощью response_model.
  • 📊 Документация стала в разы информативнее, показывая точные схемы данных.

Чертежи готовы и утверждены! В следующей главе мы перейдем от чтения данных к их созданию — реализуем полноценные CRUD-операции для нашего флота.

📌 Проверка:

  • Убедитесь, что в /docs появилась схема модели Spaceship.
  • Проверьте, что эндпоинт /spaceships теперь возвращает JSON-массив ([...]), а не объект ({...}).
  • Убедитесь, что в коде нет синтаксических ошибок после добавления моделей.

⚠️ Если ошибки:

  • NameError: name 'BaseModel' is not defined: Проверьте, что вы импортировали BaseModel из pydantic.
  • NameError: name 'List' is not defined: Проверьте, что вы импортировали List из typing.
  • Ответ на /spaceships пустой ([]): Убедитесь, что вы изменили return db_spaceships на return list(db_spaceships.values()).