← Назад к вопросам

Сталкивался ли с декларативным маппингом в SQLAlchemy

2.0 Middle🔥 111 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Декларативное маппирование в SQLAlchemy

Да, я активно работал с декларативным маппингом в SQLAlchemy. Это один из самых удобных способов определения моделей ORM в Python проектах.

Что такое декларативное маппирование?

Декларативное маппирование — это подход, при котором мы определяем классы Python и таблицы БД одновременно, используя базовый класс declarative_base(). SQLAlchemy автоматически создаёт связь между классом и таблицей.

История: от классического к декларативному

Классическое маппирование (устаревшее)

# Старый способ — раздельное определение класса и таблицы

from sqlalchemy import Table, Column, Integer, String, MetaData
from sqlalchemy.orm import mapper

metadata = MetaData()

user_table = Table(
    'users',
    metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String(50)),
    Column('email', String(100))
)

class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email

mapper(User, user_table)  # Явное маппирование

Декларативное маппирование (современный способ)

Классический декларативный синтаксис

# Декларативный подход с declarative_base()

from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.orm import declarative_base, Session
from datetime import datetime

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    
    id = Column(Integer, primary_key=True)
    name = Column(String(50), nullable=False)
    email = Column(String(100), unique=True, nullable=False)
    created_at = Column(DateTime, default=datetime.utcnow)
    
    def __repr__(self):
        return f"<User(id={self.id}, name='{self.name}')>"

Новый синтаксис SQLAlchemy 2.0

На современных версиях SQLAlchemy (2.0+) есть улучшенный синтаксис с типизацией:

from sqlalchemy import create_engine, ForeignKey, String
from sqlalchemy.orm import DeclarativeBase, Session, Mapped, mapped_column, relationship
from datetime import datetime

class Base(DeclarativeBase):
    pass

class User(Base):
    __tablename__ = 'users'
    
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(50), nullable=False)
    email: Mapped[str] = mapped_column(String(100), unique=True)
    created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
    
    posts: Mapped[list['Post']] = relationship(back_populates='user')

Практические примеры

Пример 1: E-commerce платформа

from sqlalchemy import Numeric, Boolean
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
from decimal import Decimal

class Base(DeclarativeBase):
    pass

class Product(Base):
    __tablename__ = 'products'
    
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(200))
    price: Mapped[Decimal] = mapped_column(Numeric(10, 2))
    is_active: Mapped[bool] = mapped_column(Boolean, default=True)
    
    order_items: Mapped[list['OrderItem']] = relationship(back_populates='product')

Пример 2: Каскадное удаление

class Author(Base):
    __tablename__ = 'authors'
    
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    
    # При удалении автора удаляются все его книги
    books: Mapped[list['Book']] = relationship(
        back_populates='author',
        cascade='all, delete-orphan'
    )

Проблемы и как их избежать

# Проблема 1: Циклические импорты
# Решение: использовать строковые ссылки
user: Mapped['User'] = relationship()

# Проблема 2: Лишние запросы N+1
# Решение: eager loading
users = session.query(User).options(joinedload(User.posts)).all()

Мой опыт применения

В своих проектах я:

  • Использую SQLAlchemy 2.0 синтаксис с типизацией
  • Определяю все модели в папке models/
  • Использую миграции (Alembic или Goose) вместо create_all()
  • Добавляю валидацию прямо в модель
  • Тестирую все relationships с реальной БД

Декларативное маппирование — идеальный баланс между удобством и контролем.

Сталкивался ли с декларативным маппингом в SQLAlchemy | PrepBro