Skip to main content

Validation-first SQLAlchemy queries with Pydantic row validation.

Project description

RowGuard

CI PyPI Documentation Python Versions License: MIT

RowGuard turns SQLAlchemy query results into validated Pydantic models.

By default, SQLRules (a required dependency) pushes supported model constraints into SQL so invalid candidates are filtered before fetch. Every row that is fetched is either an accepted model or an explicit rejection—never ignored after fetch.

Need to inspect invalid rows in Python? Pass use_sqlrules=False and on_reject="collect".

Use it when you already have SQLAlchemy Core tables/selects or ORM / SQLModel mapped classes and need typed reads with deterministic rejection handling. It is not an ORM and does not replace SQLAlchemy, Pydantic, or SQLModel—it validates reads over those stacks.

Status

Current release: 0.6.0 (Core + async + ORM/SQLModel + rejection platform). See Supported vs planned for what is shipped versus deferred.

Install

pip install rowguard                 # Core (includes SQLRules)
pip install "rowguard[async]"        # aiosqlite + greenlet for async
pip install "rowguard[sqlmodel]"     # SQLModel table-source support
pip install "rowguard[postgresql]"   # psycopg driver helper
Extra When you need it
(none) Sync Core / ORM reads
async aselect / astream examples and async tests
sqlmodel SQLModel mapped classes as table= / source=
postgresql Optional PostgreSQL driver
dev Contributors: pytest, ruff, mypy, …
docs Sphinx documentation build

Requires Python 3.10+, Pydantic v2, SQLAlchemy 2.x, and SQLRules ≥1.0 (3.10–3.12 tested in CI; 3.13 untested). See the installation guide.

Quickstart

Full walkthrough: Quickstart.

1. Default path (library defaults)

Invalid candidates are filtered in SQL. rejected is empty.

from typing import Annotated

from pydantic import BaseModel, Field
from sqlalchemy import Column, Integer, MetaData, String, Table, create_engine
from sqlalchemy.orm import Session

import rowguard


class UserRead(BaseModel):
    id: int
    name: str
    age: Annotated[int, Field(ge=18)]


metadata = MetaData()
users = Table(
    "users",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("name", String),
    Column("age", Integer),
)

engine = create_engine("sqlite+pysqlite:///:memory:")
metadata.create_all(engine)

with engine.begin() as connection:
    connection.execute(
        users.insert(),
        [
            {"id": 1, "name": "Ada", "age": 37},
            {"id": 2, "name": "Legacy", "age": 12},
        ],
    )

with Session(engine) as session:
    result = rowguard.select(session=session, table=users, model=UserRead)
    print(result.models)    # (UserRead(id=1, name='Ada', age=37),)
    print(result.rejected)  # ()

2. Inspect rejections in Python

with Session(engine) as session:
    result = rowguard.select(
        session=session,
        table=users,
        model=UserRead,
        on_reject="collect",      # default is "raise"
        use_sqlrules=False,       # default is True
    )
    print(result.models)    # Ada
    print(result.rejected)  # Legacy failed age >= 18

See SQLRules pushdown and the FAQ.

Public API (0.6.0)

Function Purpose
select / execute Buffered validated reads
stream Stream accepted models (no accepted-row buffer)
aselect / aexecute / astream Async counterparts
validate_rows Validate mappings without SQL
compile_plan Inspect an ExecutionPlan without executing

Rejection policies: raise (default), collect, skip, log, callback, quarantine — plus optional max_rejections / max_rejection_rate. See Rejection policies.

table= vs source=: use table= on select/stream (Core Table or mapped class). On execute with a projected Select, pass the mapped class as source=. Full parameter contracts: API guide · Python autodoc · Errors.

ORM / SQLModel guide · Streaming · Async · Performance · Upgrading.

Documentation

Start here → InstallationQuickstart.

Development

See Contributing and Security.

make install      # .[dev,async,sqlmodel] — matches CI
make all
python examples/sqlrules_default.py
python examples/basic.py

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rowguard-0.6.0.tar.gz (260.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rowguard-0.6.0-py3-none-any.whl (55.5 kB view details)

Uploaded Python 3

File details

Details for the file rowguard-0.6.0.tar.gz.

File metadata

  • Download URL: rowguard-0.6.0.tar.gz
  • Upload date:
  • Size: 260.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rowguard-0.6.0.tar.gz
Algorithm Hash digest
SHA256 6ff46a4bd006483a80f452efcf1c2bc58d579e1e2e4dca7529720b7ecb4edd27
MD5 e9aafaa0c17d03d8038a46e7bff2a49f
BLAKE2b-256 88181c825b6b14bb7eb50d7a5ff6a292e0799f4cf22bff497684c91f12262119

See more details on using hashes here.

File details

Details for the file rowguard-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: rowguard-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 55.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rowguard-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c3f30219fc13522d16ca160ee40ad1a62f62e64b5d83d94ff8f73b7352ddf54d
MD5 bef66030ceda0ff76a4ffaf7ef3bbc25
BLAKE2b-256 23039d6703b24e334c4c4b1ed1b8b0957b348b8d23c1240962163f4692d64bf0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page