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.