Найти в Дзене

Pydantic в Python: интересные практики

Pydantic — одна из ключевых библиотек в современном Python-стеке для работы с данными. Она обеспечивает валидацию, парсинг и сериализацию с использованием аннотаций типов. Вот как извлечь из неё максимум пользы. Используйте @validator для сложной логики: from pydantic import BaseModel, validator class User(BaseModel): ....username: str ....password: str ....@validator("password") ....def validate_password(cls, v): ........if len(v) < 8: ............raise ValueError("Пароль слишком короткий") ........if not any(char.isdigit() for char in v): ............raise ValueError("Пароль должен содержать цифру") ....return v user = User(username="john", password="secure123") # Корректно Используйте default_factory для генерации значений: from uuid import uuid4 from pydantic import BaseModel, Field class Document(BaseModel): ....id: str = Field(default_factory=lambda: uuid4().hex) ....content: str doc = Document(content="Hello") print(doc.id) # Например: "e4eaaaf2d3a74d3b8b0e7b8d7" Создайте
Оглавление

Pydantic — одна из ключевых библиотек в современном Python-стеке для работы с данными. Она обеспечивает валидацию, парсинг и сериализацию с использованием аннотаций типов. Вот как извлечь из неё максимум пользы.

1. Валидация с кастомными правилами

Используйте @validator для сложной логики:

from pydantic import BaseModel, validator
class User(BaseModel):
....username: str
....password: str
....@validator("password")
....def validate_password(cls, v):
........if len(v) < 8:
............raise ValueError("Пароль слишком короткий")
........if not any(char.isdigit() for char in v):
............raise ValueError("Пароль должен содержать цифру")
....return v
user = User(username="john", password="secure123") # Корректно

2. Динамические значения по умолчанию

Используйте default_factory для генерации значений:

from uuid import uuid4
from pydantic import BaseModel, Field
class Document(BaseModel):
....id: str = Field(default_factory=lambda: uuid4().hex)
....content: str
doc = Document(content="Hello")
print(doc.id) # Например: "e4eaaaf2d3a74d3b8b0e7b8d7"

3. Модели для ошибок и ответов API

Создайте унифицированные модели для API:

class ErrorResponse(BaseModel):
....error: str
....details: dict = {}
class SuccessResponse(BaseModel):
....data: dict
....message: str = "Success"
# Использование в FastAPI:
from fastapi import status, HTTPException
def handle_error(error: str, status_code: status):
....raise HTTPException(
........status_code=status_code,
........detail=ErrorResponse(error=error).dict()
....)

4. Работа с наследованием моделей

Комбинируйте модели через наследование:

class TimestampMixin(BaseModel):
....created_at: datetime = Field(default_factory=datetime.now)
....updated_at: datetime = Field(default_factory=datetime.now)
class Post(TimestampMixin):
....title: str
....content: str
post = Post(title="Hello", content="World")
print(post.created_at) # 2025-05-15 12:00:00

5. Парсинг данных из неизвестных источников

Используйте parse_obj для сырых данных:

raw_data = '{"name": "Alice", "age": "30"}' # Возможно из внешнего API
data = User.parse_raw(raw_data)
print(data) # name='Alice' age=30

6. Валидация данных с условиями

@root_validator для проверки всей модели:

class Order(BaseModel):
....items: list[str]
....discount_code: str = None
....@root_validator
....def validate_discount(cls, values):
........items, code = values.get("items"), values.get("discount_code")
........if code and len(items) < 3:
............raise ValueError("Скидка только для 3+ товаров")
....return values

7. Интеграция с ORM (SQLAlchemy, Django)

Используйте from_orm для преобразования:

from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class UserDB(Base):
....__tablename__ = "users"
....id = Column(Integer, primary_key=True)
....name = Column(String)
class UserPydantic(BaseModel):
....id: int
....name: str
....class Config:
........orm_mode = True
db_user = UserDB(name="John")
pydantic_user = UserPydantic.from_orm(db_user)

8. Работа с конфигурацией

Удобное управление настройками через BaseSettings:

from pydantic import BaseSettings, PostgresDsn
class AppSettings(BaseSettings):
....db_url: PostgresDsn
....debug: bool = False
....class Config:
........env_file = ".env"
........env_prefix = "APP_"
settings = AppSettings() # Автоматически загружает APP_DB_URL из .env

9. Кастомные типы данных

Создавайте свои типы для специфичных данных:

from pydantic import BaseModel
class RGBColor(BaseModel):
....r: int = Field(0, ge=0, le=255)
....g: int = Field(0, ge=0, le=255)
....b: int = Field(0, ge=0, le=255)
class Design(BaseModel):
....background: RGBColor = RGBColor(r=255, g=255, b=255)

10. Эволюция моделей с Field

Плавное изменение схемы:

class Product(BaseModel):
....id: int
....name: str
....# Старое поле, будет удалено в будущем
....old_field: str = Field(
........default=None,
........deprecated=True,
........description="Используйте `new_field` вместо этого"
....)

11. Валидация путей и файлов

FilePath и DirectoryPath для работы с ФС:

from pydantic import FilePath, DirectoryPath
class Config(BaseModel):
....config_path: FilePath
....log_dir: DirectoryPath
cfg = Config(config_path="/app/config.yaml", log_dir="/logs")

12. Модели для тестирования

Используйте Pydantic в тестах:

def test_user_creation():
....data = {"username": "test", "password": "secret123"}
....user = User(**data)
....assert user.username == "test"
....with pytest.raises(ValueError):
........User(username="test", password="short") # Невалидный пароль

13. Расширение через плагины

Интеграция с другими инструментами:

- pydantic-settings: Для расширенных конфигураций.

- pydantic-extra-types: Дополнительные типы данных (IBAN, MAC-адреса).

- pydantic-django: Интеграция с Django ORM.

14. Оптимизация производительности

- Pydantic V2: Написана на Rust (до 50x быстрее V1).

- Используйте model_construct() для создания объектов без валидации, когда уверены в данных:

user = User.model_construct(username="admin", password="x"*8) # Без валидации

15. Генерация схем для документации

Автоматическая генерация OpenAPI-схем в FastAPI:

from fastapi import FastAPI
app = FastAPI()
@app.post("/users/", response_model=User)
def create_user(user: User):
....return user

Схема эндпоинта будет доступна в /docs.

Заключение

Pydantic — это не просто валидатор данных, а мощный инструмент для построения надёжных и поддерживаемых приложений. Его ключевые преимущества:

- Типизация: Статический анализ с mypy.

- Безопасность: Защита от инъекций через кастомные типы.

- Интеграция: Работа с ORM, веб-фреймворками, конфигами.

- Производительность: Оптимизированные алгоритмы в V2.

Пример полной системы:

# Модель -> Настройки -> API
class DatabaseConfig(BaseSettings):
....url: PostgresDsn
class AppConfig(BaseSettings):
....db: DatabaseConfig
....debug: bool
....config = AppConfig()
class User(BaseModel):
....id: UUID
....name: str
# FastAPI эндпоинт
@app.post("/users", response_model=User)
def create_user(user: User):
....db_user = UserDB(**user.dict())
....session.add(db_user)
....session.commit()
....return User.from_orm(db_user)

Совет: Всегда обновляйтесь до последней версии Pydantic для использования современных возможностей и оптимизаций. Библитория активно развивается и становится стандартом де-факто для работы с данными в Python.

Подписывайтесь:

Телеграм https://t.me/lets_go_code
Канал "Просто о программировании"
https://dzen.ru/lets_go_code