Skip to main content

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

PyPI version Python versions Docs

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 await when 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/update or use from_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/ alongside db/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 to require_filter=True to guard against full-table writes; set to False only when intentional.
    • If you set Database(..., derive_async=False), only sync engines are created and async helpers will raise.

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

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

duo_orm-0.1.3.tar.gz (47.7 kB view details)

Uploaded Source

Built Distribution

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

duo_orm-0.1.3-py3-none-any.whl (33.4 kB view details)

Uploaded Python 3

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

Hashes for duo_orm-0.1.3.tar.gz
Algorithm Hash digest
SHA256 2c06fda080c834344fb8fbc3799e3f12411bbb64cec2e5c38a59977cd4b75a2e
MD5 0933a7268580821d545229918ad979d7
BLAKE2b-256 e82f2e41d34a835667cdc7cf4ddd4f067312568928cb8ced463f98f07d97c3c1

See more details on using hashes here.

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

Hashes for duo_orm-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 88f02a788567786b0ace0a51e590422002fb0e12a91579df05dc5fec8b93985c
MD5 3f7cd2fd3b3f5f1938064ed8ad2b1e1f
BLAKE2b-256 23c7eb03a34b68d10b4a2f52be4c9c9b1bed982da69b05cbeb05c58a69a0096c

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