Skip to main content

Python bindings for rbatis ORM - high-performance async ORM

Project description

rbatis-py

Python bindings for rbatis — a high-performance async ORM written in Rust.

Supports SQLite, MySQL, PostgreSQL, and MSSQL.

Installation

pip install rbatis-py

Requires Python ≥ 3.8.

Quick Start

Raw SQL

import asyncio
from rbatis_py import RBatis

async def main():
    db = RBatis()
    await db.link("sqlite:///path/to/test.db")

    # CREATE / INSERT / UPDATE / DELETE
    await db.exec("CREATE TABLE IF NOT EXISTS user (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")
    await db.exec("INSERT INTO user (name, age) VALUES (?, ?)", ["Alice", 30])

    # Query — returns List[Dict]
    rows = await db.exec_decode("SELECT * FROM user")
    for row in rows:
        print(row["name"], row["age"])

    # Transaction (auto mode)
    async with db.begin_defer():
        await db.exec("UPDATE user SET age = ? WHERE name = ?", [31, "Alice"])

asyncio.run(main())

CRUD via Model

from rbatis_py import RBatis, Model

class User(Model):
    __table__ = "user"
    id: int | None = None
    name: str | None = None
    age: int | None = None

async def main():
    db = RBatis()
    await db.link("sqlite:///path/to/test.db")

    await User.insert(db, {"name": "Alice", "age": 30})
    await User.insert_batch(db, [
        {"name": "Bob", "age": 25},
        {"name": "Charlie", "age": 35},
    ])
    rows = await User.select_by_map(db, {"name": "Alice"})
    affected = await User.update_by_map(db, {"age": 31}, {"name": "Alice"})
    affected = await User.delete_by_map(db, {"name": "Bob"})

API

RBatis

Method Description Rust equivalent
exec(sql, params) Execute INSERT/UPDATE/DELETE rb.exec(sql, args).await
exec_decode(sql, params) Query returns List[Dict] rb.exec_decode::<Vec<Value>>(sql, args).await
insert(table, data) Insert one row Model::insert(&rb, &table).await
insert_batch(table, data_list) Batch insert Model::insert_batch(&rb, &tables, n).await
select_by_map(table, condition) Select by condition Model::select_by_map(&rb, condition).await
update_by_map(table, data, condition) Update by condition Model::update_by_map(&rb, &table, condition).await
delete_by_map(table, condition) Delete by condition Model::delete_by_map(&rb, condition).await
link(url) Connect to database rb.link(driver, url).await
acquire() Acquire a raw connection from pool rb.acquire().await
begin() Begin transaction (explicit, returns Transaction) rb.acquire_begin().await
begin_defer() Begin transaction (auto context manager) rb.acquire_begin().await
commit() Commit current active transaction tx.commit().await
rollback() Rollback current active transaction tx.rollback().await
ping() Test connection rb.exec("SELECT 1").await
close() Close connection
set_pool_max_size(n) Set max connections in the pool pool.set_max_open_conns(n).await
set_pool_max_idle(n) Set max idle connections in the pool pool.set_max_idle_conns(n).await
set_pool_connect_timeout(s) Set connection timeout (seconds) pool.set_timeout(dur).await
set_pool_max_lifetime(s) Set max connection lifetime (seconds) pool.set_conn_max_lifetime(dur).await
pool_state() Inspect pool state (returns dict) pool.state().await

Transaction

Two transaction modes are supported:

A) Explicit (manual commit/rollback):

tx = await db.begin()
try:
    await tx.exec("INSERT INTO user (name) VALUES (?)", ["Alice"])
    await tx.exec("INSERT INTO user (name) VALUES (?)", ["Bob"])
    await tx.commit()
except Exception:
    await tx.rollback()
    raise

B) Auto (context manager):

async with db.begin_defer():
    await db.exec("INSERT INTO user (name) VALUES (?)", ["Alice"])
    # auto-commit on success, auto-rollback on exception

Connection

Acquire a raw connection from the pool to run queries in isolation:

conn = await db.acquire()
try:
    rows = await conn.exec_decode("SELECT * FROM user WHERE age > ?", [20])

    # You can also begin a transaction on this specific connection
    tx = await conn.begin()
    try:
        await tx.exec("INSERT INTO user (name) VALUES (?)", ["Alice"])
        await tx.commit()
    except Exception:
        await tx.rollback()
        raise
finally:
    await conn.close()  # return to pool

Model

Define a table model:

class User(Model):
    __table__ = "user"
    id: int | None = None
    name: str | None = None
    age: int | None = None

Then use classmethods for CRUD. The db parameter accepts RBatis, Connection, or Transaction — all support exec() / exec_decode():

await User.insert(db, {...})
await User.select_by_map(db, {condition})
await User.update_by_map(db, {set_data}, {condition})
await User.delete_by_map(db, {condition})

For example, with a raw connection:

conn = await db.acquire()
try:
    await User.insert(conn, {"name": "Alice"})
    rows = await User.select_by_map(conn, {"name": "Alice"})
finally:
    conn.close()

Type Conversion

Python types are automatically converted to/from rbatis extended types:

Python type rbs serialization Database type
datetime.datetime Ext("DateTime", ...) rbdc::DateTime
datetime.date Ext("Date", ...) rbdc::Date
datetime.time Ext("Time", ...) rbdc::Time
decimal.Decimal Ext("Decimal", ...) rbdc::Decimal
uuid.UUID Ext("Uuid", ...) rbdc::Uuid
dict / list Ext("Json", ...) rbdc::Json

Note: SQLite stores all types as TEXT; the conversion to Python types works for all databases. PostgreSQL returns timestamps as integer (milliseconds) rather than datetime objects.

Connection URLs

# SQLite
await db.link("sqlite:///path/to/db.sqlite")

# MySQL
await db.link("mysql://user:pass@localhost:3306/db")

# PostgreSQL
await db.link("postgres://user:pass@localhost:5432/db")

# MSSQL
await db.link("jdbc:sqlserver://localhost:1433;User=SA;Password=xxx;Database=db")

Connection Pool

Configure the connection pool after calling link(). These methods must be called after a connection is established.

await db.link("mysql://user:pass@localhost:3306/mydb")

# Configure pool
await db.set_pool_max_size(20)          # Max 20 connections
await db.set_pool_max_idle(5)           # Keep at most 5 idle connections
await db.set_pool_connect_timeout(30)   # Timeout waiting for a connection (seconds)
await db.set_pool_max_lifetime(3600)    # Max connection lifetime (seconds)

# Inspect pool state
state = await db.pool_state()
print(state)
# e.g. {"max_open": 20, "connections": 3, "in_use": 1, "idle": 2, "waits": 0, ...}

Development

git clone https://github.com/rbatis/rbatis-py.git
cd rbatis-py
pip install maturin
maturin develop  # build and install in current venv

Running examples

uv run python examples/basic_usage.py     # exec, exec_decode, transactions
uv run python examples/crud_usage.py      # Model CRUD
uv run python examples/crud_usage.py mysql     # with MySQL
uv run python examples/crud_usage.py postgres  # with PostgreSQL

Publishing to PyPI

# Install maturin if not already installed
pip install maturin

# Build wheel
maturin build

# Publish to PyPI (requires PyPI account and API token)
maturin publish

# Or use the official GitHub Action:
# https://github.com/PyO3/maturin-action

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

rbatis_py-0.1.2.tar.gz (41.9 kB view details)

Uploaded Source

Built Distribution

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

rbatis_py-0.1.2-cp311-abi3-win_amd64.whl (4.0 MB view details)

Uploaded CPython 3.11+Windows x86-64

File details

Details for the file rbatis_py-0.1.2.tar.gz.

File metadata

  • Download URL: rbatis_py-0.1.2.tar.gz
  • Upload date:
  • Size: 41.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.13.1

File hashes

Hashes for rbatis_py-0.1.2.tar.gz
Algorithm Hash digest
SHA256 4dcfebfed7835eb14a8c4129e54f5687f2a626cb3f19e86b9125c599e37d641f
MD5 742b21b12e04fead226e26a76da372a1
BLAKE2b-256 f8a505842cd1f4d89c5e66891f0a4ad084e95da2b6ebe9f166e72747f5ae0594

See more details on using hashes here.

File details

Details for the file rbatis_py-0.1.2-cp311-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for rbatis_py-0.1.2-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 0e49f7863ca6247cfbe3e9e18c8bffebe3f9dc3738116624294eafe76dbb12c5
MD5 60b72031e0e072a8e08fcd4345741274
BLAKE2b-256 1771940bf73f14734008e1f660cd2246582c94754b29865da8bf1343b9dc378a

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