Создание сложных структур данных через вложенные модели, списки моделей, опциональные поля.
Сложные структуры данных строятся из простых моделей как из конструктора
Pydantic позволяет вкладывать модели друг в друга:
from pydantic import BaseModel
class Address(BaseModel):
street: str
city: str
zip_code: str
country: str
class User(BaseModel):
name: str
email: str
address: Address # вложенная модель
# Создание с вложенной моделью
address = Address(
street="123 Main St",
city="New York",
zip_code="10001",
country="USA"
)
user = User(name="Alice", email="alice@example.com", address=address)
# Или сразу dict (автоматическая валидация)
user = User(
name="Alice",
email="alice@example.com",
address={
"street": "123 Main St",
"city": "New York",
"zip_code": "10001",
"country": "USA"
}
)Pydantic автоматически создаст вложенную модель из словаря.
from typing import List
class Ingredient(BaseModel):
name: str
quantity: float
unit: str
class Recipe(BaseModel):
title: str
servings: int
ingredients: list[Ingredient] # список моделей
# Создание
recipe = Recipe(
title="Pancakes",
servings=4,
ingredients=[
{"name": "Flour", "quantity": 200, "unit": "g"},
{"name": "Milk", "quantity": 300, "unit": "ml"},
{"name": "Eggs", "quantity": 2, "unit": "pcs"}
]
)
# Доступ к вложенным данным
print(recipe.ingredients[0].name) # "Flour"
print(len(recipe.ingredients)) # 3Модели могут быть вложены на любую глубину:
class Coordinate(BaseModel):
lat: float
lon: float
class Location(BaseModel):
name: str
coordinate: Coordinate
class Event(BaseModel):
title: str
location: Location
organizer: User # модель User с Address из предыдущего примера
# Глубокая вложенность
event = Event(
title="Python Conference",
location={
"name": "Convention Center",
"coordinate": {"lat": 40.7128, "lon": -74.0060}
},
organizer={
"name": "Alice",
"email": "alice@example.com",
"address": {
"street": "123 Main St",
"city": "New York",
"zip_code": "10001",
"country": "USA"
}
}
)
# Доступ
print(event.location.coordinate.lat) # 40.7128
print(event.organizer.address.city) # "New York"from typing import Optional
class Company(BaseModel):
name: str
industry: str
class User(BaseModel):
name: str
email: str
company: Optional[Company] = None # необязательная вложенная модель
# Без компании
user1 = User(name="Alice", email="alice@example.com")
print(user1.company) # None
# С компанией
user2 = User(
name="Bob",
email="bob@example.com",
company={"name": "TechCorp", "industry": "Software"}
)
print(user2.company.name) # "TechCorp"class Permission(BaseModel):
can_read: bool
can_write: bool
can_delete: bool
class Role(BaseModel):
name: str
permissions: dict[str, Permission] # словарь моделей
role = Role(
name="Admin",
permissions={
"users": {"can_read": True, "can_write": True, "can_delete": True},
"posts": {"can_read": True, "can_write": True, "can_delete": False}
}
)
print(role.permissions["users"].can_delete) # Truefrom typing import Union, Literal
class Cat(BaseModel):
pet_type: Literal["cat"]
name: str
meows: int
class Dog(BaseModel):
pet_type: Literal["dog"]
name: str
barks: float
class Owner(BaseModel):
name: str
pet: Union[Cat, Dog] # может быть Cat или Dog
# С котом
owner1 = Owner(
name="Alice",
pet={"pet_type": "cat", "name": "Whiskers", "meows": 10}
)
# С собакой
owner2 = Owner(
name="Bob",
pet={"pet_type": "dog", "name": "Buddy", "barks": 5.5}
)user = User(
name="Alice",
email="alice@example.com",
address={
"street": "123 Main St",
"city": "New York",
"zip_code": "10001",
"country": "USA"
}
)
# Полная сериализация
data = user.model_dump()
# {'name': 'Alice', 'email': 'alice@example.com',
# 'address': {'street': '123 Main St', 'city': 'New York', ...}}
# С исключением вложенного поля
data = user.model_dump(exclude={'address': True})
# {'name': 'Alice', 'email': 'alice@example.com'}
# С исключением полей во вложенной модели
data = user.model_dump(exclude={'address': {'zip_code'}})
# address без zip_code# Валидация из dict с вложенными dict
data = {
"name": "Alice",
"email": "alice@example.com",
"address": {
"street": "123 Main St",
"city": "New York",
"zip_code": "10001",
"country": "USA"
}
}
user = User.model_validate(data)
# Валидация из JSON
import json
json_str = json.dumps(data)
user = User.model_validate_json(json_str)Модель может ссылаться сама на себя:
from typing import Optional, List
from __future__ import annotations # для рекурсивных ссылок
class TreeNode(BaseModel):
value: int
left: Optional[TreeNode] = None
right: Optional[TreeNode] = None
# Создание дерева
tree = TreeNode(
value=1,
left=TreeNode(value=2, left=TreeNode(value=4)),
right=TreeNode(value=3)
)Для рекурсивных моделей в Pydantic v2 используйте from __future__ import annotations или строковую ссылку:
class Category(BaseModel):
name: str
subcategories: list['Category'] = []Создайте структуру моделей для заказа:
from pydantic import BaseModel, Field
from datetime import datetime
from typing import List, Optional
from decimal import Decimal
# OrderItem: товар в заказе
# - product_id: int
# - name: str
# - quantity: int (положительное)
# - price: Decimal (положительная)
# - discount: float (0-1, по умолчанию 0)
# ShippingAddress
# - street: str
# - city: str
# - zip_code: str
# - country: str
# Order: заказ
# - order_id: int
# - items: список OrderItem
# - shipping_address: ShippingAddress
# - billing_address: Optional[ShippingAddress] (если None, совпадает с shipping)
# - created_at: datetime (по умолчанию сейчас)
# - status: Literal["pending", "processing", "shipped", "delivered", "cancelled"]
# Добавьте метод total_price() через @computed_field для подсчёта общей суммыПример использования:
order = Order(
order_id=1,
items=[
{"product_id": 1, "name": "Mouse", "quantity": 2, "price": Decimal("29.99")},
{"product_id": 2, "name": "Keyboard", "quantity": 1, "price": Decimal("79.99")}
],
shipping_address={
"street": "123 Main St",
"city": "New York",
"zip_code": "10001",
"country": "USA"
},
status="pending"
)
print(order.total_price) # 2*29.99 + 1*79.99 = 139.97В следующей теме изучим валидацию полей с помощью Field и встроенных валидаторов.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.