SQLAlchemyJuniorTechnical

Что такое SQLAlchemy и каковы его два основных компонента (Core vs ORM)?

SQLAlchemy состоит из Core (SQL Expression Language, Engine, пул соединений) и ORM (Data Mapper, Unit of Work, управление объектами). Core ближе к SQL, ORM — к бизнес-объектам.

Что такое SQLAlchemy

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

SQLAlchemy Core

Нижний слой: абстракция над DBAPI (PEP 249). Предоставляет:

  • Engine и Connection — управление пулом соединений и транзакциями.
  • Table / Column / MetaData — описание схемы в Python без ORM-классов.
  • SQL Expression Language — конструктор SQL-запросов, который генерирует диалект-специфичный SQL.
from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, select

engine = create_engine("postgresql+psycopg://user:pass@localhost/db")
meta = MetaData()

users = Table(
    "users", meta,
    Column("id", Integer, primary_key=True),
    Column("name", String(100)),
)

# Запрос через Core (без ORM-классов):
with engine.connect() as conn:
    stmt = select(users).where(users.c.name == "Alice")
    rows = conn.execute(stmt).fetchall()
    print(rows)  # [(1, 'Alice')]

SQLAlchemy ORM

Верхний слой: реализует паттерны Data Mapper и Unit of Work. Маппит Python-классы на таблицы, управляет identity map и отслеживает изменения объектов.

from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session

class Base(DeclarativeBase):
    pass

class User(Base):
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(100))

# Создание таблицы:
Base.metadata.create_all(engine)

# CRUD через ORM:
with Session(engine) as session:
    session.add(User(name="Alice"))
    session.commit()

    user = session.get(User, 1)
    print(user.name)  # Alice

Когда что использовать

  • Core — bulk insert/update, сложные аналитические запросы (window functions, CTE), миграции (Alembic использует Core), работа с raw SQL через text().
  • ORM — CRUD-сервисы, бизнес-логика с объектной моделью, связи (relationships), валидация на уровне Python.
  • Оба слоя можно смешивать: ORM-запрос можно выполнить через session.execute(select(User)), а внутри получить Row-объекты Core.

Подводные камни

  • ORM скрывает количество SQL-запросов: N+1 при lazy loading — самая частая проблема в production.
  • Core-запросы не участвуют в identity map: изменение объекта через conn.execute(update(...)) не обновит уже загруженный ORM-объект в сессии.
  • В SQLAlchemy 2.0 session.execute(select(User)) возвращает Result, а не список — нужен явный .scalars().all() или .all().
  • create_engine() принимает строку подключения с диалектом: postgresql+psycopg:// для psycopg3, postgresql+asyncpg:// для async. Путаница с драйвером даёт невнятные ошибки.
  • MetaData нужно передавать явно или использовать Base.metadata — иначе create_all() не найдёт таблицы.
  • ORM не заменяет понимание SQL: неправильно написанный relationship() с lazy="select" в цикле даёт сотни запросов незаметно.

Common mistakes

  • Описывать core vs orm только как термин и не показывать механизм на минимальном примере.
  • Игнорировать ошибки, пустые данные, конкурентный доступ или границы транзакции.
  • Не связывать поведение с официальным контрактом SQLAlchemy и реальной эксплуатацией.

What the interviewer is testing

  • Объясняет core vs orm через последовательность действий, а не через набор ключевых слов.
  • Приводит короткий кодовый пример или production-сценарий с ожидаемым поведением.
  • Называет хотя бы один риск: производительность, безопасность, транзакции, память или сопровождение.

Sources

Related topics