An opinionated, modern ORM for Python combining the power of SQLAlchemy 2.0 with a clean, symmetrical API for sync and async operations.
Project description
DuoORM
An opinionated ORM with symmetrical sync/async APIs, explicit unit-of-work control, and ready-to-use Alembic scaffolding. DuoORM manages drivers for you: use driverless URLs like postgresql://... or sqlite:///app.db and it wires up the correct sync/async engines under the hood (SQLAlchemy 2.x).
Highlights
- One API for sync and async (add
awaitwhen needed). - Explicit unit of work: single-statement by default; opt into
db.transaction()for shared sessions and cascades. - Driverless URLs; DuoORM injects the right sync/async driver per dialect.
- CRUD helpers first:
save,create/create_bulk,update/update_bulk,delete/delete_bulk,iterate,get,count/exists,transaction. - Pydantic built-in but optional: pass schemas to
create/updateor usefrom_schema/apply_schema/to_schema; plain dicts work everywhere. - Import common SQLAlchemy types and helpers directly from
duo_orm(e.g.,String,JSON,PG_ARRAY,text,func). - Built-in Alembic CLI scaffolding and migration commands (now scaffolds
db/schemas/alongsidedb/models/). - Tested across PostgreSQL, MySQL, MSSQL, Oracle, and SQLite (coverage matrix).
Install
pip install duo-orm # core + sqlite
# Or pick your dialect
pip install "duo-orm[postgresql]" # psycopg (sync+async)
pip install "duo-orm[mysql]" # pymysql + asyncmy
pip install "duo-orm[mssql]" # pyodbc + aioodbc
pip install "duo-orm[oracle]" # oracledb (sync+async)
pip install "duo-orm[all]" # install everything
SQLite fallback (only if your Python lacks stdlib sqlite3, e.g., minimal Docker/Lambda):
pip install pysqlite3-binary
python - <<'PY'
import sys, pysqlite3
sys.modules["sqlite3"] = pysqlite3
PY
Quickstart
from duo_orm import Database, Mapped, mapped_column, String
db = Database("sqlite:///./app.db") # driverless URL
class User(db.Model):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(100))
age: Mapped[int] = mapped_column()
# One-shot read (single statement/session)
user = User.where(User.name == "Ada").first() # or await in async contexts
# Transactional work (shared session)
async def create_user():
async with db.transaction():
u = User(name="New", age=30)
await u.save()
# Convenience CRUD helpers
alice = User.create({"name": "Alice", "age": 25}) # sync create with dict
count = User.where(User.age >= 18).count() # count
async for batch in User.order_by("id").iterate(batch=True): # streaming in async
...
# Optional Pydantic (place in db/schemas/) for validated writes
from pydantic import BaseModel
class UserCreate(BaseModel):
name: str
age: int
bob = await User.create(UserCreate(name="Bob", age=28))
# When you're done (scripts/CLIs), optionally tear down engines
db.disconnect()
- Notes:
- Bulk helpers (
update_bulk/delete_bulk) default torequire_filter=Trueto guard against full-table writes; set toFalseonly when intentional. - If you set
Database(..., derive_async=False), only sync engines are created and async helpers will raise.
- Bulk helpers (
Engine lifecycle helpers
db.connect()eagerly initializes sync/async engines so misconfiguration surfaces early (optional; engines still initialize lazily).db.disconnect()disposes any initialized engines and clears cached factories; use it at the end of scripts/CLIs to release pools explicitly. Context managers (db.transaction(),standalone_session(),sync_standalone_session()) already close sessions on exit.
Documentation
- Quickstart: https://duo-orm.readthedocs.io/en/latest/quickstart/
- CRUD API: https://duo-orm.readthedocs.io/en/latest/guides/crud-api/
- Framework example: https://duo-orm.readthedocs.io/en/latest/quickstart/fastapi/
- Pydantic integration: https://duo-orm.readthedocs.io/en/latest/guides/pydantic-integration/
- Full docs & guides: https://duo-orm.readthedocs.io/
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 duo_orm-0.1.3.tar.gz.
File metadata
- Download URL: duo_orm-0.1.3.tar.gz
- Upload date:
- Size: 47.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2c06fda080c834344fb8fbc3799e3f12411bbb64cec2e5c38a59977cd4b75a2e
|
|
| MD5 |
0933a7268580821d545229918ad979d7
|
|
| BLAKE2b-256 |
e82f2e41d34a835667cdc7cf4ddd4f067312568928cb8ced463f98f07d97c3c1
|
File details
Details for the file duo_orm-0.1.3-py3-none-any.whl.
File metadata
- Download URL: duo_orm-0.1.3-py3-none-any.whl
- Upload date:
- Size: 33.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
88f02a788567786b0ace0a51e590422002fb0e12a91579df05dc5fec8b93985c
|
|
| MD5 |
3f7cd2fd3b3f5f1938064ed8ad2b1e1f
|
|
| BLAKE2b-256 |
23c7eb03a34b68d10b4a2f52be4c9c9b1bed982da69b05cbeb05c58a69a0096c
|