Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Схемы на Pydantic
Pydantic — мощная библиотека для валидации и сериализации данных на Python. Поделюсь практическими примерами схем, которые использовал.
1. Базовые модели для API
from pydantic import BaseModel, EmailStr, Field
from datetime import datetime
from typing import Optional
class UserCreate(BaseModel):
"""Схема для создания пользователя"""
name: str = Field(..., min_length=3, max_length=50)
email: EmailStr
password: str = Field(..., min_length=8)
age: Optional[int] = Field(None, ge=0, le=150)
model_config = {
"json_schema_extra": {
"examples": [{
"name": "John Doe",
"email": "john@example.com",
"password": "secure123",
"age": 30
}]
}
}
class UserResponse(BaseModel):
"""Схема для ответа пользователя"""
id: int
name: str
email: str
created_at: datetime
is_active: bool = True
model_config = {
"from_attributes": True # Работает с ORM моделями
}
2. Вложенные модели и отношения
from typing import List
class AddressModel(BaseModel):
street: str
city: str
postal_code: str
country: str
class PostModel(BaseModel):
id: int
title: str
content: str
created_at: datetime
class UserWithRelations(BaseModel):
id: int
name: str
email: str
address: AddressModel # Вложенная модель
posts: List[PostModel] # Список вложенных
model_config = {"from_attributes": True}
# Использование
user_data = {
"id": 1,
"name": "John",
"email": "john@example.com",
"address": {
"street": "123 Main St",
"city": "New York",
"postal_code": "10001",
"country": "USA"
},
"posts": [
{"id": 1, "title": "Post 1", "content": "...", "created_at": "2024-01-01T10:00:00"},
{"id": 2, "title": "Post 2", "content": "...", "created_at": "2024-01-02T10:00:00"}
]
}
user = UserWithRelations(**user_data)
print(user)
3. Валидация с field_validator
from pydantic import BaseModel, field_validator, ValidationError
class ProductCreate(BaseModel):
name: str
price: float
quantity: int
sku: str
@field_validator('price')
@classmethod
def price_must_be_positive(cls, v):
if v <= 0:
raise ValueError('Price must be greater than 0')
return round(v, 2) # Округлить до 2 знаков
@field_validator('quantity')
@classmethod
def quantity_must_be_non_negative(cls, v):
if v < 0:
raise ValueError('Quantity cannot be negative')
return v
@field_validator('sku')
@classmethod
def sku_format(cls, v):
if not v.isupper() or len(v) < 3:
raise ValueError('SKU must be uppercase with at least 3 characters')
return v
# Использование
try:
product = ProductCreate(
name="Laptop",
price=-100, # Ошибка!
quantity=5,
sku="abc" # Ошибка!
)
except ValidationError as e:
print(e.json())
4. Кастомные типы и аннотации
from pydantic import BaseModel, BeforeValidator, Field
from typing import Annotated
import re
# Кастомный телефонный номер
def validate_phone(v):
pattern = r'^\+?1?\d{9,15}$'
if not re.match(pattern, v):
raise ValueError('Invalid phone number')
return v
PhoneNumber = Annotated[str, BeforeValidator(validate_phone)]
class ContactInfo(BaseModel):
phone: PhoneNumber
email: str
# Использование
contact = ContactInfo(phone="+1234567890", email="user@example.com")
print(contact.phone) # +1234567890
5. Условная валидация с model_validator
from pydantic import BaseModel, model_validator
from datetime import datetime
class DateRange(BaseModel):
start_date: datetime
end_date: datetime
@model_validator(mode='after')
def validate_date_range(self):
if self.start_date >= self.end_date:
raise ValueError('start_date must be before end_date')
return self
class EventCreate(BaseModel):
name: str
date_range: DateRange
max_attendees: int
current_attendees: int = 0
@model_validator(mode='after')
def validate_attendees(self):
if self.current_attendees > self.max_attendees:
raise ValueError('current_attendees cannot exceed max_attendees')
return self
6. Динамические поля и Optional
from pydantic import BaseModel, Field
from typing import Optional, Dict, Any
class FilterOptions(BaseModel):
category: Optional[str] = None
min_price: Optional[float] = None
max_price: Optional[float] = None
in_stock: Optional[bool] = None
model_config = {
"json_schema_extra": {
"example": {
"category": "electronics",
"min_price": 100,
"max_price": 1000
}
}
}
class ProductFilter(BaseModel):
filters: FilterOptions
sort_by: str = "created_at"
limit: int = Field(20, ge=1, le=100)
offset: int = Field(0, ge=0)
# Использование
filter_data = {
"filters": {"category": "electronics", "min_price": 100},
"sort_by": "price",
"limit": 50
}
product_filter = ProductFilter(**filter_data)
7. Наследование и переиспользование
class TimestampModel(BaseModel):
"""Базовая модель с временными метками"""
created_at: datetime
updated_at: datetime
model_config = {"from_attributes": True}
class BaseEntity(TimestampModel):
"""Базовая сущность"""
id: int
class BlogPost(BaseEntity):
"""Пост в блоге с наследованием"""
title: str
content: str
author_id: int
is_published: bool = False
tags: List[str] = Field(default_factory=list)
class Comment(BaseEntity):
"""Комментарий с наследованием"""
post_id: int
author_id: int
text: str
is_approved: bool = False
8. Сложные типы и Generic
from pydantic import BaseModel
from typing import Generic, TypeVar, List, Dict
T = TypeVar('T')
class PaginatedResponse(BaseModel, Generic[T]):
"""Обёртка для пагинированных ответов"""
data: List[T]
total: int
page: int
page_size: int
@property
def total_pages(self) -> int:
return (self.total + self.page_size - 1) // self.page_size
# Использование
class UserInfo(BaseModel):
id: int
name: str
email: str
paginated_users = PaginatedResponse[UserInfo](
data=[
{"id": 1, "name": "John", "email": "john@example.com"},
{"id": 2, "name": "Jane", "email": "jane@example.com"}
],
total=100,
page=1,
page_size=2
)
9. Конфигурация и сериализация
from pydantic import BaseModel, Field, ConfigDict
from datetime import datetime
class ProductResponse(BaseModel):
model_config = ConfigDict(
from_attributes=True,
populate_by_name=True, # Использовать alias
str_strip_whitespace=True,
json_encoders={ # Кастомное кодирование
datetime: lambda v: v.isoformat()
}
)
id: int
name: str = Field(alias="product_name")
price: float
created_at: datetime
description: str | None = None
def to_dict(self, **kwargs):
"""Кастомная сериализация"""
return self.model_dump(**kwargs)
def to_json(self, **kwargs):
"""JSON сериализация"""
return self.model_dump_json(**kwargs)
# Использование
product = ProductResponse(
id=1,
product_name="Laptop", # Используем alias
price=999.99,
created_at=datetime.now()
)
print(product.model_dump(by_alias=True))
print(product.model_dump_json(indent=2))
10. Перечисления и Literal типы
from enum import Enum
from pydantic import BaseModel
from typing import Literal
class OrderStatus(str, Enum):
PENDING = "pending"
PROCESSING = "processing"
COMPLETED = "completed"
CANCELLED = "cancelled"
class OrderCreate(BaseModel):
items: List[int] # Product IDs
status: OrderStatus = OrderStatus.PENDING
priority: Literal["low", "medium", "high"] = "medium"
payment_method: Literal["credit_card", "paypal", "bank_transfer"]
# Использование
order = OrderCreate(
items=[1, 2, 3],
status=OrderStatus.PENDING,
payment_method="credit_card"
)
print(order.status.value) # "pending"
print(order.model_dump()) # {'items': [1, 2, 3], 'status': 'pending', ...}
11. Кастомные вычисляемые поля
from pydantic import BaseModel, computed_field
from datetime import datetime
class UserProfile(BaseModel):
first_name: str
last_name: str
birth_date: datetime
@computed_field # type: ignore[misc]
@property
def full_name(self) -> str:
return f"{self.first_name} {self.last_name}"
@computed_field # type: ignore[misc]
@property
def age(self) -> int:
return (datetime.now() - self.birth_date).days // 365
# Использование
user = UserProfile(
first_name="John",
last_name="Doe",
birth_date=datetime(1990, 1, 1)
)
print(user.full_name) # "John Doe"
print(user.age) # ~34
print(user.model_dump()) # Включает вычисляемые поля
12. Коллекции и Dict
from pydantic import BaseModel
from typing import Dict, List, Set
class AppConfig(BaseModel):
settings: Dict[str, str] # Ключ-значение
features: Dict[str, bool] # Feature flags
tags: Set[str] # Уникальные значения
metadata: Dict[str, int] # Метаданные
# Использование
config = AppConfig(
settings={"api_url": "https://api.example.com", "timeout": "30"},
features={"new_ui": True, "beta_api": False},
tags={"production", "critical", "monitored"},
metadata={"version": 1, "revision": 42}
)
print(config.features)
Лучшие практики
# 1. Разные модели для разных операций
class UserCreate(BaseModel): # Для POST
name: str
email: str
class UserUpdate(BaseModel): # Для PATCH
name: Optional[str] = None
email: Optional[str] = None
class UserResponse(BaseModel): # Для GET
id: int
name: str
email: str
created_at: datetime
# 2. Использование Field для документации
class BlogPost(BaseModel):
title: str = Field(
...,
min_length=5,
max_length=200,
description="Title of the blog post"
)
content: str = Field(..., description="Main content")
# 3. Группировка по бизнес-логике
class PaymentRequest(BaseModel):
amount: float = Field(..., gt=0)
currency: Literal["USD", "EUR", "GBP"]
card_token: str
customer_id: int
# 4. Кастомные исключения
from pydantic import ValidationError
try:
user = UserCreate(name="", email="invalid")
except ValidationError as e:
# Обработка ошибок валидации
for error in e.errors():
print(f"Field: {error['loc'][0]}, Error: {error['msg']}")
Pydantic позволяет создавать надёжные, хорошо задокументированные API с минимальным усилием. Главное — использовать типизацию и правильно структурировать модели для разных операций.