вторник, 2 сентября 2008 г.

SQLAlchemy vs SQLObject

Спросили меня, чем алхимия лучше второй альтернативы. 

Я и ответил...

-----------------------------------

Начнем с описания объекта.

В SQLObject нужно наследоваться от базового класса, внутри него создавая определения колонок и отношений. 
В SQLAlchemy используется разделение: обычный класс, к которому посредством маппера сопостовляется таблица базы данных и определяются отношения. NB: мапперов может быть много для разных целей, и это удобно. Например, "легкий" для отчетов и "тяжеловесный" для внесения изменений. Дополнительная выгода - меньше "магических" атрибутов класса, которые могут вызвать непонятные конфликты. Больше возможностей для настройки.
Свой конструктор, наконец, в котором можно делать необходимые операции для создания НОВОГО объекта, и который НЕ ВЫЗЫВАЕТСЯ при зачитывании из БД.
Описание таблицы очень похоже на CREATE STATEMENT, а mapper проецирует таблицу на пользовательский объект.

Отношения. Они не исчерпываются понятиями "один-к-одному", "один-ко-многим", "многие-ко-многим". В алхимии для классического примера "пользователь-почтовые адреса" можно задать отношение, берущее адрес по порядковому номеру (list-like). А можно и по имени почтового ящика (dict-like). Или вообще задать свой класс для коллекции. Много чего можно, при этом отношения выглядят как привычные контейнеры.

Сессии-запросы.
В SQLObject делаем настраиваем ГЛОБАЛЬНОЕ подключение к базе и делаем Person.select(...)
В алхимии создаем сессию, подключаем ее к БД (существует множество способов, равно как и типов сессий)
Потом у сессии запрашиваем query для нашего класса, и через него строим запрос. Язык запросов очень мощный и гибкий, о нем чуть позже.
Все объекты, полученные через сессию или созданные для нее, запоминаются. Все изменения запоминаются опять же. Когда приходит пора записать - один раз записываем изменения. Плюс очень мощная обработка транзакций (в рамках той же сессии, естественно). Транзакции на уровне ORM и на уровне DB.

Язык запросов. На нижнем уровне повторяет SQL, позволяя писать SELECT/UPDATE значительно более простым способом, чем "работой с типом str". Когда запросы идут от session.query - переходим на уровень выше. Из ORM можно спросить практически все, что позволяет SQL - оставаясь при этом в объектной модели. Главное - правильно мапперы настроить 

Наследование, создание новых типов колонок, connection pooling, различные способы внесения собственных hooks и прочее - присутствует, и арсенал очень богат. Но, похоже, это не тема для "вступительной статьи".


Я описывал сильные стороны алхимии, но если не требуется - легко можно редуцировать. Выйдет не сложнее, чем в SQLObject. Tutuorial для алхимии очень простой. Сила проекта - в правильно выбраных уровнях абстракции и возможности осуществить тонкую настройку практически в любом необходимом месте. Сначала все это не требуется (и даже не нужно знать, что такие возможности существуют). Но очень приятно, что для возникающих проблем уже есть решение. Приходилось упираться в ограничения собственного проекта, не позволяющего без существенной переделки сделать "то-то и это". Но в ограничения алхимии упирался очень редко, и в следующей версии они снимались.

P.S. Есть проект Elixir, напоминающий подход SQLObject, но базирующийся на SQLAlchemy. Может быть заинтересует, но я на него давно не смотрел.

P.P.S. Так получилось, что SQLAlchemy начал использовать еще в бородатой 0.1 версии. И наблюдаю за бурным развитием проекта. Сейчас уже далеко не то, что тогда, и разработчики идут правильной дорогой. Рад за них. При всей сложности задачи решается она очень хорошо, оставляя конкурентов далеко за бортом.