Skip to content

Transactions and sessions

This guide shows direct transactions, savepoints, and the session unit-of-work helper.

Use it when several writes must succeed or fail together.

What the example covers

  • async with db.transaction()
  • async with db.savepoint("name")
  • async with db.session()
  • staged inserts and session savepoints
  • commit-on-success and rollback-on-error behavior
"""Transactions, savepoints, and sessions example."""

import asyncio

from pydantic import BaseModel

from ormdantic import Ormdantic

db = Ormdantic("sqlite:///examples_transactions_sessions.sqlite3")


@db.table(pk="id")
class Flavor(BaseModel):
    id: str
    name: str


async def main() -> None:
    await db.init()
    await db.drop_all()
    await db.create_all()

    async with db.transaction():
        await db[Flavor].insert(Flavor(id="1", name="mocha"))
        try:
            async with db.savepoint("optional_insert"):
                await db[Flavor].insert(Flavor(id="2", name="rollback"))
                raise RuntimeError("rollback only the savepoint")
        except RuntimeError:
            pass

    async with db.session() as session:
        try:
            async with session.savepoint("optional_session_insert"):
                session.add(Flavor(id="2", name="discarded"))
                await session.flush()
                raise RuntimeError("rollback only the session savepoint")
        except RuntimeError:
            pass
        session.add(Flavor(id="3", name="vanilla"))

    assert await db[Flavor].count() == 2


if __name__ == "__main__":
    asyncio.run(main())

Run it locally:

python examples/transactions_sessions.py

Choose the right API

Use direct transactions when you already know each operation. Use sessions when you want to stage model objects and let Ormdantic flush them in dependency order.