Lightweight Python query builder for asyncpg. Use ? placeholders and querymark handles the $1, $2, $3 conversion.
Project description
querymark
Lightweight Python query builder for asyncpg. Use ? placeholders and querymark handles the $1, $2, $3 conversion. Compose queries with + operator while keeping parameters tracked automatically.
The Problem
When managing indexed parameters in SQL queries to use for asyncpg for example you can easily get into problems when conditionally constructing queries.
For example if the number of conditions needed in the query is based on some logic you could end up with something like this
if role and extended_role:
check = "(role <= $1 AND extended_role <= $2)"
params = [role, extended_role]
elif role:
check = "role <= $1"
params = [role]
elif extended_role:
check = "extended_role <= $1"
params = [extended_role]
else:
raise Exception("Must specify either role, extended_role or both")
if project:
query = f"""
SELECT 1 FROM user_project_roles
WHERE user_id = ${len(params) + 1} AND {check}
AND (project_id = ${len(params) + 2} OR project_id IS NULL)
LIMIT 1
"""
result = await db.fetchrow(query, *params, user.id, project.id)
return result is not None
else:
query = f"""
SELECT 1 FROM user_project_roles
WHERE user_id = ${len(params) + 1} AND {check}
AND project_id IS NULL
LIMIT 1
"""
result = await db.fetchrow(query, *params, user.id)
return result is not None
The Solution
With querymark, you write queries using ? placeholders and let the library handle parameter indexing:
from querymark import q
# Build conditions naturally
if role and extended_role:
check = q("(role <= ? AND extended_role <= ?)", role, extended_role)
elif role:
check = q("role <= ?", role)
elif extended_role:
check = q("extended_role <= ?", extended_role)
else:
raise Exception("Must specify either role, extended_role or both")
# Compose the full query
query = q("SELECT 1 FROM user_project_roles")
query += q("WHERE user_id = ?", user.id) + " AND " + check
if project:
query += q("AND (project_id = ? OR project_id IS NULL)", project.id)
result = await db.fetchrow(*query.to_sql())
return result is not None
Installation
pip install querymark
(Note: Package will be available on PyPI soon. For now, install from source.)
Basic Usage
from querymark import q
# Simple query with parameters
query = q("SELECT * FROM users WHERE id = ?", user_id)
result = await db.fetch(*query.to_sql())
# Compose queries dynamically
base = q("SELECT id, name, email FROM users")
if filter_active:
base += q("WHERE active = ?", True)
if sort_by:
base += f"ORDER BY {sort_by}"
results = await db.fetch(*base.to_sql())
# Functions can return query fragments
def group_filter(self) -> q:
return q("group_id = ?", self.group_id)
@staticmethod
def to_query() -> q:
return q("SELECT id, group_id, text, published FROM article")
# And then combined when needed
rows = await db.fetch(
Article.to_query()
+ q("WHERE") + article.group_filter()
+ "ORDER BY published"
)
For more examples and detailed documentation, see USAGE.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file querymark-1.1.0.tar.gz.
File metadata
- Download URL: querymark-1.1.0.tar.gz
- Upload date:
- Size: 5.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f56c89b03ce104dc7a18202a5e8e3fe1ce8aa075896b3a5fc23ff50d20e8d1a
|
|
| MD5 |
f9b1a691fac6bd583b8e70778407ffff
|
|
| BLAKE2b-256 |
cc8a6fdd85a32f5f9a7d1ee824b8402d77b75c475b5cbc060e201c04942b183b
|
File details
Details for the file querymark-1.1.0-py3-none-any.whl.
File metadata
- Download URL: querymark-1.1.0-py3-none-any.whl
- Upload date:
- Size: 5.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
27e3ea1b25559fa330e1981c3572bb1a3dfbfa7ad19465795dbef18bef92c54a
|
|
| MD5 |
89c5e031daa39f2798590aa481594129
|
|
| BLAKE2b-256 |
a5ba90f6ecd4af7ee7c853d5e8ba59e765bfbcfc475cc5695c33d7ed3f4938a2
|