Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Интеграция с SQLAlchemy: ORM-модели и Pydantic
sqlalchemy_integration

Интеграция с SQLAlchemy: ORM-модели и Pydantic

Создание Pydantic-моделей на основе SQLAlchemy, режим from_attributes, DTO-паттерн.

Интеграция с SQLAlchemy: ORM-модели и Pydantic

Pydantic и SQLAlchemy — мощная комбинация. Изучим, как связать ORM-модели с Pydantic-валидацией

#Базовая интеграция

SQLAlchemy ORM-модели не совместимы с Pydantic напрямую. Нужно использовать from_attributes:

from sqlalchemy import Column, Integer, String from sqlalchemy.orm import DeclarativeBase from pydantic import BaseModel, ConfigDict # SQLAlchemy модель class Base(DeclarativeBase): pass class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True) name = Column(String) email = Column(String) # Pydantic модель class UserSchema(BaseModel): model_config = ConfigDict(from_attributes=True) id: int name: str email: str # Использование user_orm = User(id=1, name="Alice", email="alice@example.com") user_schema = UserSchema.model_validate(user_orm) print(user_schema.name) # "Alice"

#Разделение моделей: ORM vs Pydantic

from sqlalchemy import Column, Integer, String, DateTime from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column from datetime import datetime from pydantic import BaseModel, ConfigDict, EmailStr # SQLAlchemy ORM модель class Base(DeclarativeBase): pass class UserORM(Base): __tablename__ = "users" id: Mapped[int] = mapped_column(primary_key=True) username: Mapped[str] = mapped_column(unique=True) email: Mapped[str] password_hash: Mapped[str] created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow) is_active: Mapped[bool] = mapped_column(default=True) # Pydantic модели для разных операций class UserCreate(BaseModel): """Для создания пользователя""" username: str email: EmailStr password: str class UserUpdate(BaseModel): """Для обновления — все поля optional""" username: str | None = None email: EmailStr | None = None password: str | None = None is_active: bool | None = None class UserResponse(BaseModel): """Для ответа — без password_hash""" model_config = ConfigDict(from_attributes=True) id: int username: str email: str created_at: datetime is_active: bool class UserInDB(UserResponse): """Для внутреннего использования — с password_hash""" password_hash: str

#DTO паттерн с SQLAlchemy

from typing import Optional, List from pydantic import BaseModel, ConfigDict # SQLAlchemy модели class PostORM(Base): __tablename__ = "posts" id: Mapped[int] = mapped_column(primary_key=True) title: Mapped[str] content: Mapped[str] author_id: Mapped[int] = mapped_column(ForeignKey("users.id")) author: Mapped["UserORM"] = relationship("UserORM") comments: Mapped[List["CommentORM"]] = relationship("CommentORM") # Pydantic DTO class PostBase(BaseModel): title: str content: str class PostCreate(PostBase): author_id: int class PostResponse(PostBase): model_config = ConfigDict(from_attributes=True) id: int author_id: int created_at: datetime updated_at: datetime | None class PostWithAuthor(PostResponse): author: "UserResponse" class PostWithComments(PostResponse): comments: list["CommentResponse"]

#Валидация при создании

from sqlalchemy.orm import Session def create_user(db: Session, user: UserCreate) -> UserORM: """Создание пользователя с валидацией""" # Проверка на дубликат email existing = db.query(UserORM).filter(UserORM.email == user.email).first() if existing: raise ValueError("Email already registered") # Проверка на дубликат username existing = db.query(UserORM).filter(UserORM.username == user.username).first() if existing: raise ValueError("Username already taken") # Создание hashed_password = hash_password(user.password) db_user = UserORM( username=user.username, email=user.email, password_hash=hashed_password ) db.add(db_user) db.commit() db.refresh(db_user) return db_user # Использование user_create = UserCreate(username="alice", email="alice@example.com", password="secret123") user_orm = create_user(db, user_create) user_response = UserResponse.model_validate(user_orm)

#Валидация при обновлении

def update_user(db: Session, user_id: int, user_update: UserUpdate) -> UserORM: """Обновление пользователя с валидацией""" db_user = db.query(UserORM).filter(UserORM.id == user_id).first() if not db_user: raise ValueError("User not found") # Обновление только переданных полей update_data = user_update.model_dump(exclude_unset=True) # Если пароль обновляется — хэшируем if "password" in update_data: update_data["password_hash"] = hash_password(update_data.pop("password")) for key, value in update_data.items(): setattr(db_user, key, value) db.commit() db.refresh(db_user) return db_user # Использование user_update = UserUpdate(email="new@example.com", is_active=False) updated_user = update_user(db, 1, user_update)

#Связи и вложенные модели

from pydantic import BaseModel, ConfigDict from typing import List, Optional class CommentResponse(BaseModel): model_config = ConfigDict(from_attributes=True) id: int post_id: int content: str created_at: datetime class PostResponse(BaseModel): model_config = ConfigDict(from_attributes=True) id: int title: str content: str author_id: int comments: List[CommentResponse] = [] # Исключаем comments из рекурсивной сериализации class Config: from_attributes = True # Загрузка с relationship post_orm = db.query(PostORM).options( joinedload(PostORM.comments) ).first() post_response = PostResponse.model_validate(post_orm) print(post_response.comments) # [CommentResponse(...), ...]

#Query параметры и фильтрация

from pydantic import BaseModel, Field from typing import Optional, List from enum import Enum class UserStatus(str, Enum): active = "active" inactive = "inactive" all = "all" class UserFilter(BaseModel): """Фильтр для поиска пользователей""" status: UserStatus = UserStatus.active min_id: int | None = Field(None, gt=0) max_id: int | None = Field(None, gt=0) email_domain: str | None = None limit: int = Field(ge=1, le=100, default=10) offset: int = Field(ge=0, default=0) def filter_users(db: Session, filter: UserFilter) -> List[UserORM]: """Применение фильтра к query""" query = db.query(UserORM) if filter.status != UserStatus.all: query = query.filter(UserORM.is_active == (filter.status == UserStatus.active)) if filter.min_id: query = query.filter(UserORM.id >= filter.min_id) if filter.max_id: query = query.filter(UserORM.id <= filter.max_id) if filter.email_domain: query = query.filter(UserORM.email.like(f"%@{filter.email_domain}")) return query.offset(filter.offset).limit(filter.limit).all()

#Сериализация больших данных

from pydantic import BaseModel, ConfigDict, computed_field class UserSummary(BaseModel): """Лёгкая версия для списков""" model_config = ConfigDict(from_attributes=True) id: int username: str @computed_field @property def display_name(self) -> str: return f"@{self.username}" class UserDetail(BaseModel): """Полная версия для детального просмотра""" model_config = ConfigDict(from_attributes=True) id: int username: str email: str created_at: datetime is_active: bool @computed_field @property def display_name(self) -> str: return f"@{self.username}" @computed_field @property def account_age_days(self) -> int: return (datetime.now() - self.created_at).days

#Мини-проект: Blog API с SQLAlchemy

Создайте полный набор моделей для блога:

from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey from sqlalchemy.orm import relationship, DeclarativeBase, Mapped, mapped_column from pydantic import BaseModel, ConfigDict, Field, EmailStr from datetime import datetime from typing import List, Optional # SQLAlchemy модели class Base(DeclarativeBase): pass class Author(Base): __tablename__ = "authors" id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] email: Mapped[str] bio: Mapped[str | None] posts: Mapped[List["Post"]] = relationship("Post", back_populates="author") class Post(Base): __tablename__ = "posts" id: Mapped[int] = mapped_column(primary_key=True) title: Mapped[str] content: Mapped[str] author_id: Mapped[int] = mapped_column(ForeignKey("authors.id")) created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow) updated_at: Mapped[datetime | None] author: Mapped["Author"] = relationship("Author", back_populates="posts") # Pydantic модели: # AuthorCreate, AuthorUpdate, AuthorResponse # PostCreate, PostUpdate, PostResponse, PostWithAuthor # Функции: # create_author(db, author: AuthorCreate) -> Author # get_author(db, author_id: int) -> AuthorResponse # create_post(db, post: PostCreate) -> Post # get_posts_by_author(db, author_id: int) -> List[PostResponse]

Далее: Продвинутая сериализация: сериализаторы и контекст