Migrating to v5.0¶
This page covers every breaking change introduced in v5.0 and the steps required to update your code.
Database¶
db.py is now the db/ package, built around one object, Database, that owns the engine and sessionmaker. The free functions that took a session_maker you built and passed around yourself are gone from request-handling code; Database builds the sessionmaker for you.
create_db_dependency / create_db_context removed in favor of Database¶
Build one Database with your URL (or an existing engine=), then use the instance directly as the FastAPI dependency, and db.session() for sessions outside request handlers.
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
from fastapi_toolsets.db import create_db_dependency, create_db_context
engine = create_async_engine("postgresql+asyncpg://...")
SessionLocal = async_sessionmaker(engine, expire_on_commit=False)
get_db = create_db_dependency(session_maker=SessionLocal)
get_db_context = create_db_context(session_maker=SessionLocal)
@app.get("/users")
async def list_users(session: AsyncSession = Depends(get_db)):
...
async def seed():
async with get_db_context() as session:
...
Call db.install(app) to also commit before the response is sent (instead of in dependency teardown) and to dispose the engine on shutdown. See the db module docs.
get_transaction renamed to transaction¶
Same behavior (savepoint when already in a transaction, new transaction otherwise), new name, same import path.
If you have a Database instance, db.begin() opens a session already inside a transaction:
lock_tables is now also a Database method¶
The free lock_tables(session_maker, tables, ...) function still exists for callers who manage their own session factory, but prefer db.lock_tables(tables, ...), which drops the session_maker argument:
create_database and cleanup_tables moved to fastapi_toolsets.db.testing¶
Fixtures¶
get_obj_by_attr / get_field_by_attr are now FixtureRegistry methods¶
Both also change their first argument: instead of the fixture function, pass the fixture's registered name and let the registry look it up.
from fastapi_toolsets.fixtures import get_obj_by_attr, get_field_by_attr
@fixtures.register(depends_on=["roles"])
def users():
admin_role = get_obj_by_attr(fixtures=roles, attr_name="name", value="admin")
admin_role_id = get_field_by_attr(fixtures=roles, attr_name="name", value="admin")
return [User(id=1, username="alice", role_id=admin_role.id)]
Loading/listing by context now always includes Context.BASE¶
FixtureRegistry.get_variants/get_by_context, load_fixtures_by_context, and the fixtures list/fixtures load CLI commands now implicitly union every queried context with Context.BASE. In v4, requesting a single context (e.g. "testing") returned only fixtures tagged with that context; in v5 it also returns every Context.BASE-tagged fixture.
If you relied on strict exclusion of base fixtures, move them out of Context.BASE into their own context.
load_fixtures / load_fixtures_by_context now return reloaded instances¶
Loaded objects are re-queried from the database after insert, with all relationships eager-loaded, instead of returning the exact objects your fixture function constructed. Code that compares returned objects by identity to the fixture function's output, or that expected relationships to remain unloaded, should be updated — and expect one extra query per model type per load.
Security¶
The security module has been removed and moved to a dedicated python package: fastapi-multiauth.
Run uv add fastapi-multiauth and replace from fastapi_toolsets.security import ... with from fastapi_multiauth import ....