# Генерация SQL-запросов используя Jinja-шаблон #
Идея библиотеки заключается в попытке работать с SQL-запросами как с шаблонами Jinja.
Вдохновлено [embrace](https://pypi.org/project/embrace/) и
[jinjasql](https://pypi.org/project/jinjasql/), оттуда же бралась часть кода.
## Установка:
```shell
pip install classic-sql-tools
```
## Quickstart:
```python
from classic.sql_tools import Module
import psycopg
# Модуль - точка входа во все функции библиотеки.
# При инстанцировании запоминает указанный путь,
# дальнейшие обращения будут
queries = Module('path/to/sql/templates/dir')
# Создадим подключение к БД
conn = psycopg.connect('posgresql:///some_db')
# Применим схему:
queries.tasks.ddl(conn)
# Сохранение данных
queries.tasks.save_task(conn, [
{'title': 'Some Task', 'body': 'Do something'},
{'title': 'Another Task', 'body': 'Do anything'},
])
# Получение данных
task = queries.tasks.get_by_id(conn, id=1).one()
# (1, 'Some Task', 'Do something')
```
В директории sql рядом с .py файлом надо разместить 3 файла
(можно найти в директории test/example):
`sql/tasks/ddl.sql`:
```sql
CREATE TABLE tasks (
id serial PRIMARY KEY,
title text,
body text
);
```
`sql/tasks/get_by_id.sql`:
```sql
SELECT id, title, body FROM tasks WHERE id = {{ id }};
```
`sql/tasks/save_task.sql`:
```sql
INSERT INTO tasks (title, body) VALUES ({{ title }}, {{ body }});
```
## Возможности
```python
# Класс Module - точка входа во все функции библиотеки.
from classic.sql_tools import Module
# При инстанцировании ему обязательно нужно
# передать путь до директории с шаблонами.
queries = Module('path/to/sql')
# Затем можно получить шаблон запроса, лежащего,
# например, в `./sql/some_file.sql`:
query = queries.some_file
# Module поддерживает обращение имен директорий в Python.
# То есть объект-запрос можно получить,
# обратившись к атрибуту Module с названием директории.
# Например, файл, лежащий в `./sql/some_dir/some_file.sql`:
query = queries.some_dir.some_file
#Вложенность может быть любой:
query = queries.some_dir.another_dir.etc.some_file
#Также можно получить объект запроса,
#напрямую обратившись по его относительному пути:
query = queries.from_file('sql/some_dir/some_file.sql')
# И можно получить объект запроса из строкового литерала:
query = queries.from_str('SELECT id FROM tasks')
# После получения объект запроса можно выполнить
# с использованием соединения или курсора:
import psycopg
conn = psycopg.connect('posgresql:///some_db')
result = queries.tasks.get_by_id(conn)
# Либо
cursor = conn.cursor()
result = queries.tasks.get_by_id(cursor)
# Также можно выполнить запрос через метод .execute:
query = queries.tasks.get_by_id
query.execute(conn)
# Объект результата нужен для удобного представления
# результатов запроса. У него есть методы для представления набора строк,
# единичных строк и единичных значений -
# .many(), .one() и .scalar() соответственно
# Вернет список кортежей:
result = queries.from_str('SELECT * FROM tasks').execute(conn)
print(result.many())
# Вернет до 100 строк. При повторном вызове вернет следующие 100:
result = queries.from_str('SELECT * FROM tasks').execute(conn)
print(result.many(100))
print(result.many(100))
# Вернет один кортеж или None:
result = queries.from_str(
'SELECT * FROM tasks WHERE id = 1'
).execute(conn)
print(result.one())
# Вернет один кортеж или исключение ValueError,
# если в БД ничего не нашлось:
result = queries.from_str('SELECT * FROM tasks WHERE id = 1').execute(conn)
print(result.one(raising=True))
# Вернет один кортеж или None:
result = queries.from_str(
'SELECT id FROM tasks WHERE id = 1'
).execute(conn).scalar()
print(result.scalar())
# Аналогично .one() вернет один кортеж или
# исключение ValueError, если в БД ничего не нашлось:
result = queries.from_str('SELECT id FROM tasks WHERE id = 1').execute(conn)
print(result.scalar(raising=True))
```
Raw data
{
"_id": null,
"home_page": null,
"name": "classic-sql-tools",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "Jinja2, SQL, Python, Template",
"author": null,
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/cd/0f/a0d9ace7b56ad201c6ac71052e0f6da5280ceffc88abf8e680d289edc33b/classic_sql_tools-0.4.3.tar.gz",
"platform": null,
"description": "# \u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f SQL-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f Jinja-\u0448\u0430\u0431\u043b\u043e\u043d #\n\n\u0418\u0434\u0435\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0437\u0430\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 SQL-\u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438 \u043a\u0430\u043a \u0441 \u0448\u0430\u0431\u043b\u043e\u043d\u0430\u043c\u0438 Jinja.\n\u0412\u0434\u043e\u0445\u043d\u043e\u0432\u043b\u0435\u043d\u043e [embrace](https://pypi.org/project/embrace/) \u0438 \n[jinjasql](https://pypi.org/project/jinjasql/), \u043e\u0442\u0442\u0443\u0434\u0430 \u0436\u0435 \u0431\u0440\u0430\u043b\u0430\u0441\u044c \u0447\u0430\u0441\u0442\u044c \u043a\u043e\u0434\u0430.\n\n## \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430:\n```shell\npip install classic-sql-tools\n```\n\n## Quickstart:\n\n```python\nfrom classic.sql_tools import Module\nimport psycopg\n\n# \u041c\u043e\u0434\u0443\u043b\u044c - \u0442\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430 \u0432\u043e \u0432\u0441\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.\n# \u041f\u0440\u0438 \u0438\u043d\u0441\u0442\u0430\u043d\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u0442 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u043f\u0443\u0442\u044c,\n# \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0438\u0435 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442\nqueries = Module('path/to/sql/templates/dir')\n\n# \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0411\u0414\nconn = psycopg.connect('posgresql:///some_db')\n\n# \u041f\u0440\u0438\u043c\u0435\u043d\u0438\u043c \u0441\u0445\u0435\u043c\u0443:\nqueries.tasks.ddl(conn)\n\n# \u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445\nqueries.tasks.save_task(conn, [\n {'title': 'Some Task', 'body': 'Do something'},\n {'title': 'Another Task', 'body': 'Do anything'},\n])\n\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445\ntask = queries.tasks.get_by_id(conn, id=1).one()\n# (1, 'Some Task', 'Do something')\n```\n\n\u0412 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 sql \u0440\u044f\u0434\u043e\u043c \u0441 .py \u0444\u0430\u0439\u043b\u043e\u043c \u043d\u0430\u0434\u043e \u0440\u0430\u0437\u043c\u0435\u0441\u0442\u0438\u0442\u044c 3 \u0444\u0430\u0439\u043b\u0430\n(\u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 test/example):\n\n`sql/tasks/ddl.sql`:\n```sql\nCREATE TABLE tasks (\n id serial PRIMARY KEY,\n title text,\n body text\n);\n```\n\n`sql/tasks/get_by_id.sql`:\n```sql\nSELECT id, title, body FROM tasks WHERE id = {{ id }};\n```\n\n`sql/tasks/save_task.sql`:\n```sql\nINSERT INTO tasks (title, body) VALUES ({{ title }}, {{ body }});\n```\n\n## \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438\n\n\n```python\n# \u041a\u043b\u0430\u0441\u0441 Module - \u0442\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430 \u0432\u043e \u0432\u0441\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.\nfrom classic.sql_tools import Module\n\n\n# \u041f\u0440\u0438 \u0438\u043d\u0441\u0442\u0430\u043d\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0435\u043c\u0443 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0443\u0436\u043d\u043e \n# \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u043f\u0443\u0442\u044c \u0434\u043e \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0441 \u0448\u0430\u0431\u043b\u043e\u043d\u0430\u043c\u0438.\nqueries = Module('path/to/sql')\n\n# \u0417\u0430\u0442\u0435\u043c \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0448\u0430\u0431\u043b\u043e\u043d \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043b\u0435\u0436\u0430\u0449\u0435\u0433\u043e,\n# \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 `./sql/some_file.sql`:\nquery = queries.some_file\n\n# Module \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u0438\u043c\u0435\u043d \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0439 \u0432 Python.\n# \u0422\u043e \u0435\u0441\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442-\u0437\u0430\u043f\u0440\u043e\u0441 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c, \n# \u043e\u0431\u0440\u0430\u0442\u0438\u0432\u0448\u0438\u0441\u044c \u043a \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0443 Module \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438.\n# \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0444\u0430\u0439\u043b, \u043b\u0435\u0436\u0430\u0449\u0438\u0439 \u0432 `./sql/some_dir/some_file.sql`:\nquery = queries.some_dir.some_file\n\n#\u0412\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043b\u044e\u0431\u043e\u0439:\nquery = queries.some_dir.another_dir.etc.some_file\n\n#\u0422\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \n#\u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u043e\u0431\u0440\u0430\u0442\u0438\u0432\u0448\u0438\u0441\u044c \u043f\u043e \u0435\u0433\u043e \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u043c\u0443 \u043f\u0443\u0442\u0438:\nquery = queries.from_file('sql/some_dir/some_file.sql')\n\n# \u0418 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438\u0437 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u043e\u0433\u043e \u043b\u0438\u0442\u0435\u0440\u0430\u043b\u0430:\nquery = queries.from_str('SELECT id FROM tasks')\n\n# \u041f\u043e\u0441\u043b\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \n# \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u043a\u0443\u0440\u0441\u043e\u0440\u0430:\nimport psycopg\n\nconn = psycopg.connect('posgresql:///some_db')\n\nresult = queries.tasks.get_by_id(conn)\n# \u041b\u0438\u0431\u043e\ncursor = conn.cursor()\nresult = queries.tasks.get_by_id(cursor)\n\n# \u0422\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u0447\u0435\u0440\u0435\u0437 \u043c\u0435\u0442\u043e\u0434 .execute:\nquery = queries.tasks.get_by_id\nquery.execute(conn)\n\n# \u041e\u0431\u044a\u0435\u043a\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \n# \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0423 \u043d\u0435\u0433\u043e \u0435\u0441\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u0430\u0431\u043e\u0440\u0430 \u0441\u0442\u0440\u043e\u043a, \n# \u0435\u0434\u0438\u043d\u0438\u0447\u043d\u044b\u0445 \u0441\u0442\u0440\u043e\u043a \u0438 \u0435\u0434\u0438\u043d\u0438\u0447\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 - \n# .many(), .one() \u0438 .scalar() \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\n\n# \u0412\u0435\u0440\u043d\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u043a\u043e\u0440\u0442\u0435\u0436\u0435\u0439:\nresult = queries.from_str('SELECT * FROM tasks').execute(conn)\nprint(result.many())\n\n# \u0412\u0435\u0440\u043d\u0435\u0442 \u0434\u043e 100 \u0441\u0442\u0440\u043e\u043a. \u041f\u0440\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u043c \u0432\u044b\u0437\u043e\u0432\u0435 \u0432\u0435\u0440\u043d\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 100:\nresult = queries.from_str('SELECT * FROM tasks').execute(conn)\nprint(result.many(100))\nprint(result.many(100))\n\n# \u0412\u0435\u0440\u043d\u0435\u0442 \u043e\u0434\u0438\u043d \u043a\u043e\u0440\u0442\u0435\u0436 \u0438\u043b\u0438 None:\nresult = queries.from_str(\n 'SELECT * FROM tasks WHERE id = 1'\n).execute(conn)\nprint(result.one())\n\n# \u0412\u0435\u0440\u043d\u0435\u0442 \u043e\u0434\u0438\u043d \u043a\u043e\u0440\u0442\u0435\u0436 \u0438\u043b\u0438 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 ValueError, \n# \u0435\u0441\u043b\u0438 \u0432 \u0411\u0414 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043d\u0430\u0448\u043b\u043e\u0441\u044c:\nresult = queries.from_str('SELECT * FROM tasks WHERE id = 1').execute(conn)\nprint(result.one(raising=True))\n\n# \u0412\u0435\u0440\u043d\u0435\u0442 \u043e\u0434\u0438\u043d \u043a\u043e\u0440\u0442\u0435\u0436 \u0438\u043b\u0438 None:\nresult = queries.from_str(\n 'SELECT id FROM tasks WHERE id = 1'\n).execute(conn).scalar()\nprint(result.scalar())\n\n# \u0410\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e .one() \u0432\u0435\u0440\u043d\u0435\u0442 \u043e\u0434\u0438\u043d \u043a\u043e\u0440\u0442\u0435\u0436 \u0438\u043b\u0438 \n# \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 ValueError, \u0435\u0441\u043b\u0438 \u0432 \u0411\u0414 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043d\u0430\u0448\u043b\u043e\u0441\u044c:\nresult = queries.from_str('SELECT id FROM tasks WHERE id = 1').execute(conn)\nprint(result.scalar(raising=True))\n```\n",
"bugtrack_url": null,
"license": null,
"summary": null,
"version": "0.4.3",
"project_urls": null,
"split_keywords": [
"jinja2",
" sql",
" python",
" template"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "cd0fa0d9ace7b56ad201c6ac71052e0f6da5280ceffc88abf8e680d289edc33b",
"md5": "60514a6ec65410a229eb4912a3a8cc69",
"sha256": "d8d21e3398e127c3f30b7baec0fbeb2e9c2fb6e0aaebd001d0480541fb0cf207"
},
"downloads": -1,
"filename": "classic_sql_tools-0.4.3.tar.gz",
"has_sig": false,
"md5_digest": "60514a6ec65410a229eb4912a3a8cc69",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 11793,
"upload_time": "2025-08-25T06:22:17",
"upload_time_iso_8601": "2025-08-25T06:22:17.691061Z",
"url": "https://files.pythonhosted.org/packages/cd/0f/a0d9ace7b56ad201c6ac71052e0f6da5280ceffc88abf8e680d289edc33b/classic_sql_tools-0.4.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-25 06:22:17",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "classic-sql-tools"
}