Сегодня мы углубимся в создание сложных моделей с несколькими уровнями вложенности. Это позволит вам эффективно обрабатывать данные даже в объёмных и комплексных структурах, таких как JSON, где вложенность неизбежна. Да, JSON-структуры могут быть такими же замысловатыми, как очередной способ программиста уклониться от комментариев в коде, но вместе мы справимся!
В реальных проектах вы часто сталкиваетесь с ситуациями, когда необходимо работать не с одномерными данными (например, просто именем пользователя или возрастом), а с более сложными структурами. Представьте себе, что вам нужно создать API для регистрации компании, её сотрудников и их контактной информации. Эти данные имеют вложенную структуру. Pydantic позволяет описывать и валидировать такие структуры данных простыми и читаемыми моделями.
Эти навыки пригодятся вам:
- При разработке API для сложных систем (например, CRM, ERP);
- При интеграции с внешними сервисами, где используются сложные JSON-ответы;
- При проектировании модели данных в микросервисной архитектуре.
Создаём сложную модель
Давайте начнём с примера. Допустим, мы создаём API для системы управления проектами. У нас есть сущности: проект, задачи в проекте и ответственные за задачи пользователи.
Вот пример структуры данных, которую мы хотим получить:
{
"id": 1,
"name": "FastAPI Development",
"tasks": [
{
"id": 101,
"title": "Set up project",
"completed": false,
"assigned_to": {
"id": 501,
"name": "Alice",
"email": "alice@example.com"
}
},
{
"id": 102,
"title": "Add authentication",
"completed": true,
"assigned_to": {
"id": 502,
"name": "Bob",
"email": "bob@example.com"
}
}
]
}
Приступим к реализации модели.
Базовые модели
Сначала создадим модель для пользователя, так как она используется в задаче.
from pydantic import BaseModel, EmailStr
class User(BaseModel):
id: int
name: str
email: EmailStr
Здесь всё просто: id — это идентификатор пользователя, name — его имя, а email — электронная почта (мы используем тип EmailStr, чтобы автоматически валидировать формат email).
Теперь создадим модель для задачи.
from typing import Optional
from pydantic import BaseModel
class Task(BaseModel):
id: int
title: str
completed: bool
assigned_to: User | None # Поле может быть None
Модель Task содержит:
id— идентификатор задачи;title— название задачи;completed— булевое значение, отражающее статус задачи;assigned_to— информация о пользователе, которому назначена задача. Обратите внимание, что это поле имеет типUser! Это и есть вложенность. Поле указано какOptional, т.е. задача может быть без назначенного пользователя.
Модель проекта
Теперь создадим модель для проекта, которая включает список задач.
from typing import List
class Project(BaseModel):
id: int
name: str
tasks: List[Task] # Список задач
Модель Project содержит:
id— идентификатор проекта;name— название проекта;tasks— список объектовTask. ИспользуемList[Task]из модуляtyping.
Пример сложной модели
Соединим всё вместе и протестируем.
from fastapi import FastAPI
app = FastAPI()
@app.post("/projects/")
def create_project(project: Project):
return {"message": "Project successfully created!", "data": project}
Запустите приложение с помощью uvicorn. Вот пример запроса, который мы можем отправить:
{
"id": 1,
"name": "FastAPI Development",
"tasks": [
{
"id": 101,
"title": "Set up project",
"completed": false,
"assigned_to": {
"id": 501,
"name": "Alice",
"email": "alice@example.com"
}
},
{
"id": 102,
"title": "Add authentication",
"completed": true,
"assigned_to": {
"id": 502,
"name": "Bob",
"email": "bob@example.com"
}
}
]
}
И вы получите следующий ответ:
{
"message": "Project successfully created!",
"data": {
"id": 1,
"name": "FastAPI Development",
"tasks": [
{
"id": 101,
"title": "Set up project",
"completed": false,
"assigned_to": {
"id": 501,
"name": "Alice",
"email": "alice@example.com"
}
},
{
"id": 102,
"title": "Add authentication",
"completed": true,
"assigned_to": {
"id": 502,
"name": "Bob",
"email": "bob@example.com"
}
}
]
}
}
Работа с валидацией в сложных структурах
Pydantic автоматически проверяет каждое поле на соответствие указанному типу. Если какое-либо поле не соответствует, будет выброшена ошибка валидации.
Например, отправим запрос с неправильным email у пользователя:
{
"id": 1,
"name": "FastAPI Development",
"tasks": [
{
"id": 101,
"title": "Set up project",
"completed": false,
"assigned_to": {
"id": 501,
"name": "Alice",
"email": "alice_at_example.com"
}
}
]
}
Ответ:
{
"detail": [
{
"loc": ["body", "tasks", 0, "assigned_to", "email"],
"msg": "value is not a valid email address",
"type": "value_error.email"
}
]
}
Это сообщение об ошибке показывает путь, где именно возникла ошибка: в первом элементе списка tasks в поле email.
Полезные советы
- Сложные структуры лучше разбивать на отдельные модели. Не пытайтесь "запихнуть всё в одну модель". Это усложняет её поддержку.
- Используйте описательные имена. Названия моделей и полей должны быть понятными.
- Тестируйте вложенные структуры. Иногда ошибка в одном из уровней может быть неочевидной.
Применение в реальных проектах
Сложные вложенные модели находят применение в любой системе, работающей с иерархическими данными. Например:
- Системы управления задачами (Trello, Jira);
- E-commerce платформы (товары, категории, магазины);
- Интеграция с внешними API, особенно когда данные содержат вложенные JSON-объекты.
Теперь вы знаете, как проектировать и валидировать сложные модели с помощью Pydantic. Готовы к задачам реального мира? Поехали в следующие лекции!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ