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-сценарий с ожидаемым поведением.
- Называет хотя бы один риск: производительность, безопасность, транзакции, память или сопровождение.