Skip to main content

sqlalchemy-querytools

Lightweight helpers to build SQLAlchemy SELECT queries with common list-table features: search, sort, and * pagination*.
Each tool accepts a sqlalchemy.Select and returns a modified Select. Designed to be composed — ideal for APIs, CRUD endpoints and list pages.

Version: 0.1.0
License: MIT


Table of contents


Purpose

sqlalchemy-querytools provides three small, composable builders that make it easy to add common list-table features to SQLAlchemy queries:

  • SearchQuery — builds WHERE clauses for text/date searching across model fields.
  • SortQuery — builds ORDER BY with ASC/DESC and controls null placement.
  • PaginationQuery — applies LIMIT and OFFSET.

Each builder returns a sqlalchemy.Select that you then execute with your session.


Install

If published to PyPI:

pip install sqlalchemy-querytools

Or install locally:

pip install -e .

Requirements: SQLAlchemy (version compatible with your project). For running tests: pytest.


Quick example

from sqlalchemy import select
from sqlalchemy.orm import sessionmaker

from sqlalchemy_querytools import SearchQuery, SortQuery, PaginationQuery
from sqlalchemy_querytools.types import SearchType, SortType

# assume engine, Session, and User model are already defined
session = Session()

table_fields = {
    "name": User.name,
    "email": User.email,
    "created_at": User.created_at,
}

stmt = select(User)

# search -> sort -> paginate
stmt = SearchQuery(stmt, SearchType.ALL_FIELDS, table_fields, "alice").build()
stmt = SortQuery(stmt, SortType.ASC, table_fields, "name", null_last=False).build()
stmt = PaginationQuery(stmt, offset=0, limit=20).build()

users = session.execute(stmt).scalars().all()

Public API

Exports (from __all__):

  • SearchQuery(stmt: Select, query_tool_type: SearchType, table_fields_relation: TableFields, search_value: str)
    • .build() -> Select
  • SortQuery(stmt: Select, query_tool_type: SortType, table_fields_relation: TableFields, sort_field: str, null_last: bool = False)
    • .build() -> Select
  • PaginationQuery(stmt: Select, offset: int, limit: int)
    • .build() -> Select
  • SearchType — enum values: ALL_FIELDS, DATE_FIELDS
  • SortType — enum values: ASC, DESC
  • TableFields — dict[str, InstrumentedAttribute] (mapping names → SQLAlchemy attributes)

Behavior details

SearchQuery

  • SearchType.ALL_FIELDS
    • Builds ilike('%value%') for each mapped field.
    • If a mapped field is non-text, the implementation casts to String before applying ilike.
    • If search_value is empty or whitespace, no filters are added and the original Select is returned.
  • SearchType.DATE_FIELDS
    • Expects search_value parseable via datetime.fromisoformat (e.g. "2020-01-01"), converts to date and compares equality against Date/DateTime fields.
    • Invalid date string → ValueError from datetime.fromisoformat.
  • Unknown SearchType → TypeError("Uncorrected search type").

SortQuery

  • Looks up sort_field in table_fields_relation. Missing key → KeyError.
  • SortType.ASC → asc(field); SortType.DESC → desc(field).
  • null_last=True uses .nullslast(), otherwise .nullsfirst().
  • Unknown SortType → TypeError("Uncorrected sort type").

PaginationQuery

  • Applies .limit(limit).offset(offset) on the Select.

Examples

Search (ALL_FIELDS)

from sqlalchemy_querytools import SearchQuery
from sqlalchemy_querytools.types import SearchType

stmt = select(User)
search = SearchQuery(stmt, SearchType.ALL_FIELDS, {"name": User.name, "email": User.email}, "ann")
result = session.execute(search.build()).scalars().all()

Search (DATE_FIELDS)

search = SearchQuery(select(User), SearchType.DATE_FIELDS, {"created_at": User.created_at}, "2020-01-01")
rows = session.execute(search.build()).scalars().all()
# Note: the date string must be ISO-like and parseable by datetime.fromisoformat

Sort (ASC / DESC) and null placement

from sqlalchemy_querytools import SortQuery
from sqlalchemy_querytools.types import SortType

# ascending, NULLs first (default)
q = SortQuery(select(User), SortType.ASC, {"name": User.name}, "name", null_last=False)

# descending, NULLs last
q2 = SortQuery(select(User), SortType.DESC, {"name": User.name}, "name", null_last=True)

If sort_field is not in table_fields_relation, _use_extension() will raise KeyError — this makes invalid input explicit.

Pagination

from sqlalchemy_querytools import PaginationQuery

page = PaginationQuery(select(User), offset=20, limit=10)
stmt = page.build()
rows = session.execute(stmt).scalars().all()

Compose: search + sort + paginate

stmt = select(User)
stmt = SearchQuery(stmt, SearchType.ALL_FIELDS, table_fields, "alice").build()
stmt = SortQuery(stmt, SortType.ASC, table_fields, "name", null_last=True).build()
stmt = PaginationQuery(stmt, offset=0, limit=25).build()
rows = session.execute(stmt).scalars().all()

Tests

Project includes pytest tests which demonstrate expected behaviors:

  • Case-insensitive partial search
  • Unicode / Cyrillic handling
  • Special characters and emoji support
  • Date searching behavior (including tests that skip if implementation raises ValueError)
  • Sorting behavior with NULL values

Run tests:

pip install -r requirements_dev.txt  # sqlalchemy, pytest, etc.
pytest -q

Notes / gotchas

  • ilike and case-insensitivity depend on your DB backend and column collations.
  • SearchQuery casts non-text fields to String for ALL_FIELDS search; ensure table_fields_relation lists only intended columns to search.
  • Date searching uses datetime.fromisoformat; invalid input raises ValueError. Tests are aware of this behavior.
  • Tools return Select objects; you must execute them with your session (e.g. session.execute(stmt).scalars().all()).
  • SortQuery intentionally raises KeyError for missing fields to fail fast with user-provided sort parameters.

Contributing

  1. Fork the repo.
  2. Create a branch for your change.
  3. Add tests for new behaviors or fixes.
  4. Open a pull request.

Please include tests for DB-sensitive behavior (e.g. collation, null ordering) where applicable.


License

This project is licensed under the MIT License — see LICENSE in the repository for the full text.

MIT License

Copyright (c) 2025

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction...

Metadata

Release files for sqlalchemy-querytools 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sqlalchemy-querytools 0.1.2
File Size Uploaded
sqlalchemy_querytools-0.1.2.tar.gz 6.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sqlalchemy-querytools 0.1.2
File Interpreter ABI Platform
sqlalchemy_querytools-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 14.5 kB

Release files / sqlalchemy_querytools-0.1.2.tar.gz

Download URL sqlalchemy_querytools-0.1.2.tar.gz
Size 6.3 kB
Tags Source
SHA-256 checksum
How to use checksums
91cff67caf5cce35dc95797ebcf229a52880c4acd5425f7c0fd699802415b639
BLAKE2b-256 checksum
How to use checksums
30448e13de1ee71a232e51b5284b6606b95c1e1ae85032efe8a77b377ffa3318
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.3

Release files / sqlalchemy_querytools-0.1.2-py3-none-any.whl

Download URL sqlalchemy_querytools-0.1.2-py3-none-any.whl
Size 8.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8254f5961ec61ecdcc601aca69c92cef9b02b5abdd09f9dadb7993d88e3924bc
BLAKE2b-256 checksum
How to use checksums
c1b4effe48b77b4e2f5a0277753b8b693ec767e43b6b620a57bbe9246c3b220a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.3

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page