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

# https://pypi.org/project/rbatis-py/
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.3.tar.gz (48.3 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.3-cp311-abi3-macosx_11_0_arm64.whl (17.1 MB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

File details

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

File metadata

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

File hashes

Hashes for rbatis_py-0.1.3.tar.gz
Algorithm Hash digest
SHA256 e8c199dfe502529924316f4bd4e4dafde615e13ae7985a2e4836879c31a79959
MD5 243a1dba756d074229ec276947bdb882
BLAKE2b-256 ceea89ec33fd7b014b40a91b31a28d60a615b6b3a7ecb0dec65cfdabf273cb8c

See more details on using hashes here.

File details

Details for the file rbatis_py-0.1.3-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for rbatis_py-0.1.3-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 155fc60761a334473c3da9d57642caa1dba50f6e64e9504f55d41a3c61faa5a0
MD5 e3cc3a23275961ede33fa4cf20e21dbb
BLAKE2b-256 adbf5df9e1d40fccec8ae3e3c8981aab41cf353b19c642e4d7a1afd01d17568a

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