⚖️ SQLAlchemy: из лекции (старое) → современное

Лекции курса написаны на SQLAlchemy 1.x API (классический стиль: declarative_base(), sessionmaker(bind=...), session.query()). Современный SQLAlchemy 2.x поддерживает этот стиль через слой совместимости, однако рекомендует новый. В основных уроках используется актуальный код. Здесь — сравнение.

1. Создание базового класса

Из лекции (1.x, старое)Современное (2.x)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from sqlalchemy.orm import declarative_base
Base = declarative_base()
# или (2.x native):
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
    pass

2. Определение модели: Column vs Mapped

Из лекции (1.x, старое)Современное (2.x)
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    age = Column(Integer)
from sqlalchemy.orm import Mapped, mapped_column

class User(Base):
    __tablename__ = 'users'
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(50))
    age: Mapped[int | None] = mapped_column(default=None)

⚠️ Оба стиля работают в SQLAlchemy 2.x. Для новых проектов предпочтительнее Mapped + mapped_column — он типобезопасен и поддерживается IDE.

3. Создание сессии

Из лекции (1.x, старое)Современное (2.x)
Session = sessionmaker(bind=engine)
session = Session()
# Вариант A: тот же стиль (совместим)
Session = sessionmaker(bind=engine)
session = Session()

# Вариант B: контекстный менеджер (2.x рекомендует)
with Session(engine) as session:
    ...

# Вариант C: scoped_session (для Flask)
from sqlalchemy.orm import scoped_session
db_session = scoped_session(sessionmaker(bind=engine))

4. Запросы: Query API vs select()

Из лекции (1.x, старое)Современное (2.x)
# Все записи
session.query(User).all()

# С фильтром
session.query(User).filter(User.age > 25).all()

# Первый элемент
session.query(User).first()
from sqlalchemy import select

# Все записи
result = session.execute(select(User))
users = result.scalars().all()

# С фильтром
result = session.execute(
    select(User).where(User.age > 25)
)
users = result.scalars().all()

# Первый элемент
user = session.scalars(select(User)).first()

Примечание: session.query() (Legacy Query API) поддерживается в SQLAlchemy 2.x и не будет удалён в ближайшее время. Лекционный код работает без изменений. Новый стиль с select() более явен и поддерживает async.

5. Классический vs декларативный маппинг

Классический маппинг (из лекции)Декларативный (рекомендуется)
from sqlalchemy.orm import registry

Register = registry()
user_table = Table('users', Register.metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String),
)
class User:
    pass
Register.map_imperatively(User, user_table)
Register.create_all(engine)
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)

Base.metadata.create_all(engine)

Классический маппинг полезен при работе с legacy-схемами или строгом разделении модели и таблицы. Декларативный — стандарт для большинства проектов.

6. Удаление по фильтру

Из лекции (1.x, старое)Современное (2.x)
session.query(User).filter(
    User.name == "Alice"
).delete()
from sqlalchemy import delete

session.execute(
    delete(User).where(User.name == "Alice")
)
session.commit()