Почему используете SQLAlchemy ORM вместо raw SQL запросов?
Что проверяют
Не любовь к ORM, а понимание размена. Интервьюер хочет услышать, что вы знаете, за что платите абстракцией, и умеете спускаться на уровень SQL, когда это оправдано. Ответ в духе «ORM удобнее» без продолжения читается как отсутствие опыта с нагруженными запросами.
Как отвечать
Перечислите то, что ORM даёт на самом деле, а не то, что звучит красиво. Схема и модель описаны в одном месте, сессия ведёт учёт изменённых объектов и пишет их одной транзакцией, identity map не даёт получить два разных объекта для одной строки, связи описываются один раз и переиспользуются, значения автоматически уходят параметрами, а Alembic ведёт миграции по тем же моделям.
Обязательно скажите, что SQLAlchemy это два слоя. Core даёт язык выражений и умеет работать вообще без маппинга, ORM живёт поверх него, а сырой запрос доступен через text() в той же сессии и транзакции. То есть выбор не бинарный.
stmt = select(User).where(User.email == email).options(selectinload(User.orders))
user = session.scalars(stmt).one_or_none()Рабочая формулировка для собеседования: CRUD и доменная логика на ORM, тяжёлая аналитика и массовые операции на Core или сыром SQL, критичные запросы проверяются планом выполнения независимо от того, кто их построил.
Где спотыкаются
Не называют N+1. Обращение к связи внутри цикла превращает один запрос в сотни, и это самая частая проблема любого ORM. Лечится жадной загрузкой, selectinload или joinedload. В асинхронном режиме ленивая подгрузка вообще не работает и падает, поэтому связи приходится планировать заранее.
Считают, что ORM защищает от инъекций при любом использовании. Параметризация работает для значений, но text() со склеенной строкой или сортировка по имени колонки из запроса пользователя оставляют ровно ту же дыру.
Ставят переносимость между СУБД главным аргументом. На практике проект живёт на одной базе, а всё интересное, upsert, JSONB, оконные функции, пишется под конкретный диалект. Полезнее знать про жизненный цикл сессии: после коммита атрибуты по умолчанию считаются устаревшими и следующее чтение идёт новым запросом, а сессия, живущая весь процесс, держит транзакцию и копит объекты.