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. .. code-block:: python # 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") .. code-block:: yaml # seeds/employees.yaml model: app.models.Employee data: - name: John Smith '!company': model: app.models.Company data: name: MyCompany .. code-block:: python 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: .. code-block:: python 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 :doc:`CLI ` in your deploy or container entrypoint instead of the application process. Testing FastAPI apps -------------------- The bundled :doc:`pytest plugin ` works unchanged; only the ``engine`` fixture differs — build the schema from ``SQLModel.metadata``: .. code-block:: python # 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 .. code-block:: python # 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 :doc:`pytest` for the full fixture reference. Command line ------------ Model paths in seed files (and ``--model`` for CSV) resolve SQLModel classes like any other: .. code-block:: console $ 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 :doc:`async`: .. code-block:: python 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.