Рассматривая стек Luminus, я наткнулся на простую и в то же время шикарную, на мой вкус, библиотеку Yesql для организации SQL-запросов в проекте на Clojure и я не увидел чего-то похожего для Python (может плохо искал). Идея этой библиотеки простая — не морочьте себе голову, используйте обычные SQL-запросы, у вас есть возможность именования этих запросов и мапинга на соответствующие динамические функции. Всё это выглядит как набор микро-шаблонов с SQL и их рендер по какому-то контексту. Просто, эффективно, хочу такое у себя в проекте на Python.
Вообще в последнее время мне импонирует мысль, что ORM не нужны. Они переусложняют, на самом деле, работу с реляционными БД, скрывают «адский» SQL за ширмой сложных конструкций собственных объектов, а зачастую выдают и крайне неэффективный результат. Наверняка кто-то поспорит с этим выводом, но моя практика показала, что Django ORM ужасающе простой чуть более чем всегда (и доступен только если вы используете Django, конечно), SQLAlchemy ужасающе сложный, Peewee — ни разу не встречал в дикой природе, к тому же ещё немного и он станет как Alchemy по своему порогу вхождения. SQL — сам по себе мощный и выразительный DSL, вам не нужен ещё один уровень абстракции над ним, серьёзно. Под другим углом я задумался о целесообразности ORM во время очередного проекта на Tornado. Алхимия чудесным алхимическим образом убивает всю асинхронность выполнения обработчика блокирующими вызовами в базу. И вариантов кроме как использовать тот же Momoko с сырыми запросами я не увидел.
Всё, что нам нужно для полного счастья — это разведение SQL-строк и Python-кода по разным углам и некоторая гибкость в построении конструкций по условиям или контексту. Ну и перестать бояться писать SQL, конечно. Изучить SQL до необходимого уровня реально проще чем все нюансы Алхимии для того же результата.
Попробовав и немного переосмыслив Yesql у меня родилась крохотная библиотека Snaql, которая решает описанную выше проблему, хоть и немного по-своему. Я решил вообще не завязываться на клиенты к базам и использовать Jinja2 в качестве движка для парсинга и рендеринга шаблонов с SQL-блоками (со всеми вытекающими возможностями использовать её шаблонную логику). Вот как это выглядит.
1. Ставим Snaql.
$ pip install snaql
2. Создаём в своём проекте папку, куда будем складывать файлы с SQL-блоками. Или несколько таких папок.
/queries
users.sql
3. В users.sql у нас, например, все запросы, связанные с сущностью пользователя.
{% sql 'users_by_country', note='counts users' %}
SELECT count(*) AS count
FROM user
WHERE country_code = ?
{% endsql %}
Как можно догадаться, SQL помещается внутри блока {%sql%}{%endsql%}, «users_by_country» это название функции, на которую навешивается данный SQL (создаётся динамически), а «note» — это docstring к этой функции, он опционален.
Таких блоков в одном файле может быть сколь угодно много. Главное, чтобы их имена были уникальны.
4. Теперь нам нужна фабрика, которая распарсит такие файлы и создаст набор одноимённых функций.
from snaql.factory import Snaql
# корень проекта
root_location = os.path.abspath(os.path.dirname(__file__))
# регистрация директории с шаблонами
snaql_factory = Snaql(root_location, 'queries')
# регистрация шаблона с SQL-блоками
# users_queries = snaql_factory.load_queries('users.sql')
Извлечь в коде необходимый SQL теперь можно просто вызвав
your_sql = users_queries.users_by_country()
# SELECT count(*) AS count
# FROM user
# WHERE country_code = ?
На самом деле уже этого может быть достаточно. Но не в случае с генерируемыми условиями запроса. В этом случае можно добавить в шаблон всю то логику, которую предоставляет Jinja. Например:
{% sql 'users_select_cond', note='select users with condition' %}
SELECT *
FROM user
{% if users_ids %}
WHERE user_id IN ({{ users_ids|join(', ') }})
{% endif %}
{% endsql %}
Если вызвать функцию без контекста:
your_sql = users_queries.users_select_cond()
# SELECT *
# FROM user
И если с контекстом:
your_sql = users_queries.users_select_cond(users_ids=[1, 2, 3])
# SELECT *
# FROM user
# WHERE user_id IN (1, 2, 3)
Получив сформированный SQL, остальное — дело техники. Вроде неплохо, да? В любом случае пишите свои «за» и «против» в комментариях, мне интересно мнение сообщества, насколько это может быть удобным кому-то кроме меня.
This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
Комментариев нет:
Отправить комментарий