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
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e8c199dfe502529924316f4bd4e4dafde615e13ae7985a2e4836879c31a79959
|
|
| MD5 |
243a1dba756d074229ec276947bdb882
|
|
| BLAKE2b-256 |
ceea89ec33fd7b014b40a91b31a28d60a615b6b3a7ecb0dec65cfdabf273cb8c
|
File details
Details for the file rbatis_py-0.1.3-cp311-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: rbatis_py-0.1.3-cp311-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 17.1 MB
- Tags: CPython 3.11+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
155fc60761a334473c3da9d57642caa1dba50f6e64e9504f55d41a3c61faa5a0
|
|
| MD5 |
e3cc3a23275961ede33fa4cf20e21dbb
|
|
| BLAKE2b-256 |
adbf5df9e1d40fccec8ae3e3c8981aab41cf353b19c642e4d7a1afd01d17568a
|