4.5 KiB
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.
=== "Before (v4)"
```python
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:
...
```
=== "Now (v5)"
```python
from fastapi_toolsets.db import Database
db = Database(url="postgresql+asyncpg://...")
@app.get("/users")
async def list_users(session: AsyncSession = Depends(db)):
...
async def seed():
async with db.session() 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.
=== "Before (v4)"
```python
from fastapi_toolsets.db import get_transaction
async with get_transaction(session=session):
session.add(model)
```
=== "Now (v5)"
```python
from fastapi_toolsets.db import transaction
async with transaction(session=session):
session.add(model)
```
If you have a Database instance, db.begin() opens a session already inside a transaction:
async with db.begin() as session:
session.add(User(name="ada"))
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:
=== "Before (v4)"
```python
from fastapi_toolsets.db import lock_tables, LockMode
async with lock_tables(session_maker=session_maker, tables=[Order], mode=LockMode.EXCLUSIVE) as session:
await process_order(session, order_id)
```
=== "Now (v5)"
```python
from fastapi_toolsets.db import LockMode
async with db.lock_tables(tables=[Order], mode=LockMode.EXCLUSIVE) as session:
await process_order(session, order_id)
```
create_database and cleanup_tables moved to fastapi_toolsets.db.testing
=== "Before (v4)"
```python
from fastapi_toolsets.db import create_database, cleanup_tables
```
=== "Now (v5)"
```python
from fastapi_toolsets.db.testing import create_database, cleanup_tables
```
Fixtures
get_obj_by_attr / get_field_by_attr are now FixtureRegistry methods
=== "Before (v4)"
```python
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(roles, "name", "admin")
return [User(id=1, username="alice", role_id=admin_role.id)]
```
=== "Now (v5)"
```python
@fixtures.register(depends_on=["roles"])
def users():
admin_role = fixtures.obj("roles", "name", "admin")
return [User(id=1, username="alice", role_id=admin_role.id)]
```
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 ....