FastAPI & SQLModel

SQLModel table models are SQLAlchemy models — a class declared with table=True is a regular SQLAlchemy mapped class underneath. sqlalchemyseed therefore works with SQLModel and FastAPI out of the box: the same seed files, the same seeders, the same CLI and pytest plugin. This page shows the FastAPI-shaped entry points.

Requires sqlmodel>=0.0.22 (older releases pin a SQLAlchemy version this library does not support).

Models and seed files

Point the model key at your SQLModel class, exactly as you would for a declarative model. sqlmodel.Session subclasses sqlalchemy.orm.Session, so seeders accept it directly.

# app/models.py
from typing import Optional

from sqlmodel import Field, Relationship, SQLModel


class Company(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str

    employees: list["Employee"] = Relationship(back_populates="company")


class Employee(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    company_id: Optional[int] = Field(default=None, foreign_key="company.id")

    company: Optional[Company] = Relationship(back_populates="employees")
# seeds/employees.yaml
model: app.models.Employee
data:
  - name: John Smith
    '!company':
      model: app.models.Company
      data:
        name: MyCompany
from sqlmodel import Session, create_engine

from sqlalchemyseed import Seeder, load_entities_from_yaml

engine = create_engine("sqlite:///database.db")

with Session(engine) as session:
    seeder = Seeder(session)
    seeder.seed(load_entities_from_yaml("seeds/employees.yaml"))
    session.commit()

Seeding on application startup

For demo or development data, seed inside FastAPI’s lifespan hook:

from contextlib import asynccontextmanager

from fastapi import FastAPI
from sqlmodel import Session, SQLModel, create_engine

from sqlalchemyseed import Seeder, load_entities_from_yaml

engine = create_engine("sqlite:///database.db")


@asynccontextmanager
async def lifespan(app: FastAPI):
    SQLModel.metadata.create_all(engine)
    with Session(engine) as session:
        seeder = Seeder(session)
        seeder.seed(load_entities_from_yaml("seeds/employees.yaml"))
        session.commit()
    yield


app = FastAPI(lifespan=lifespan)

For production reference data, prefer running the CLI in your deploy or container entrypoint instead of the application process.

Testing FastAPI apps

The bundled pytest plugin works unchanged; only the engine fixture differs — build the schema from SQLModel.metadata:

# conftest.py
import pytest
from sqlalchemy.pool import StaticPool
from sqlmodel import SQLModel, create_engine

import app.models  # noqa: F401 — registers tables on SQLModel.metadata


@pytest.fixture(scope="session")
def engine():
    engine = create_engine(
        "sqlite://",
        connect_args={"check_same_thread": False},
        poolclass=StaticPool,
    )
    SQLModel.metadata.create_all(engine)
    return engine
# test_people.py
from sqlalchemy import select

from app.models import Employee


def test_employees_are_seeded(seed, sqlalchemyseed_session):
    seed("seeds/employees.yaml")
    employee = sqlalchemyseed_session.scalars(select(Employee)).one()
    assert employee.company.name == "MyCompany"

Every test runs in a transaction that is rolled back afterward — see Testing with pytest for the full fixture reference.

Command line

Model paths in seed files (and --model for CSV) resolve SQLModel classes like any other:

$ sqlalchemyseed seeds/ --url sqlite:///database.db
$ sqlalchemyseed people.csv --model app.models.Employee --url sqlite:///database.db

Async

AsyncSeeder and AsyncHybridSeeder accept SQLModel’s async session — see Async usage:

from sqlmodel.ext.asyncio.session import AsyncSession

from sqlalchemyseed import AsyncSeeder, load_entities_from_yaml

async def seed_db(engine):
    async with AsyncSession(engine) as session:
        seeder = AsyncSeeder(session)
        await seeder.seed(load_entities_from_yaml("seeds/employees.yaml"))
        await session.commit()

Troubleshooting

Unknown attribute raises AttributeError — a seed file naming an attribute the model does not have raises AttributeError for SQLModel and plain declarative models alike. The fix is the same either way: correct the attribute name in the seed file.

Forgot table=True — a SQLModel class without table=True is not a mapped class; using it as a model raises UnsupportedClassError. Add table=True to the class definition.