Skip to content

Quickstart

This guide creates two related tables, inserts data, queries it, and previews a migration diff.

Create A Database

from pydantic import BaseModel, Field

from ormdantic import Ormdantic, TableForeignKey, selectinload

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

Define Tables

@db.table(pk="id", indexed=["name"])
class Supplier(BaseModel):
    id: str
    name: str = Field(min_length=2)


@db.table(
    pk="id",
    indexed=["name"],
    foreign_key_constraints=[
        TableForeignKey(
            name="flavor_supplier_fk",
            columns=["supplier"],
            foreign_table="supplier",
            foreign_columns=["id"],
            on_delete="cascade",
        )
    ],
)
class Flavor(BaseModel):
    id: str
    name: str
    supplier: Supplier | str | None = None

Supplier and Flavor are normal Pydantic models. The decorators register database metadata with db.

Create Schema

async def setup() -> None:
    await db.init()

init() builds the native runtime and creates registered tables, namespaces, sequences, and views.

Insert Rows

async def seed() -> None:
    supplier = await db[Supplier].insert(
        Supplier(id="s1", name="North Roasters")
    )
    await db[Flavor].insert(
        Flavor(id="f1", name="Vanilla", supplier=supplier)
    )

db[Model] returns a Table[Model] handle. The handle owns CRUD and query methods for that model.

Query Rows

async def query() -> None:
    flavors = await db[Flavor].find_many(
        {"name": {"like": "Van%"}},
        order_by=["name"],
        load=[selectinload("supplier")],
    )

    for flavor in flavors.data:
        print(flavor.name, flavor.supplier.name if flavor.supplier else None)

Use dictionary filters for simple queries and expression helpers for composed SQL.

Use A Session

async def with_session() -> None:
    async with db.session() as session:
        supplier = Supplier(id="s2", name="South Roasters")
        flavor = Flavor(id="f2", name="Mocha", supplier_id="s2")
        session.add(supplier)
        session.add(flavor)

The session is a small async unit-of-work wrapper. It flushes staged changes and commits on successful context exit.

Preview A Migration

def preview_migration() -> list[str]:
    before = db.migrations.live_snapshot()
    after = db.migrations.snapshot()
    return db.migrations.dry_run(before=before, after=after)

Use migrations when the database already exists and you want planned SQL instead of create_all().