Приходилось ли дописывать функционал ORM SQLAlchemy самостоятельно?
Что проверяют
Знаете ли вы штатные точки расширения SQLAlchemy или лезете править её поведение наугад. Библиотека специально сделана слоями, и сильный ответ обычно звучит так: дописывать ядро не понадобилось, хватило предусмотренных крючков.
Как отвечать
Перечислите инструменты и приведите один живой пример.
TypeDecoratorдля своего типа колонки: скажем, деньги лежат в базе целым числом минимальных единиц, а в Python приходят отдельным объектом;hybrid_propertyиhybrid_method, когда выражение должно работать и на загруженном объекте, и внутриselect;- система событий:
before_flush,after_commit,do_orm_executeдля перехвата любых выборок; with_loader_criteria, чтобы условие вроде мягкого удаления или принадлежности арендатору подмешивалось само, в том числе к связанным сущностям;@compilesиFunctionElement, если нужна конструкция, которой в диалекте нет.
Пример с мягким удалением объясняет разницу подходов. Наивный путь это свой класс запроса на все случаи жизни. Аккуратный путь это обработчик do_orm_execute, который подмешивает with_loader_criteria ко всем выборкам, кроме помеченных явным признаком.
Полезно упомянуть соглашение об именах ограничений через MetaData: без него автогенерация миграций начинает придумывать имена сама, и на разных базах они расходятся.
Где спотыкаются
Подменяют класс запроса или сессии глобально. Дальше ломаются загрузчики связей и подзапросы, потому что ваше условие приклеивается и туда, где его никто не ждал.
Второе: кладут инициализацию в метод __init__ модели. При загрузке строки из базы он не вызывается, объект получается наполовину пустым, и баг виден только на прочитанных записях. Нужен @reconstructor или обработчик события загрузки.
Третье: делают запросы внутри before_flush. Автоматический сброс срабатывает повторно, получается вложенный flush, а после неудачи сессия остаётся в нерабочем состоянии, и следующий запрос падает уже без внятной причины.