mirror of
https://github.com/d3vyce/fastapi-toolsets.git
synced 2026-08-04 15:44:09 +00:00
162 lines
5.8 KiB
Markdown
162 lines
5.8 KiB
Markdown
# 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`](../reference/db.md#fastapi_toolsets.db.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](../module/db.md#committing-before-the-response).
|
|
|
|
### `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:
|
|
|
|
```python
|
|
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
|
|
|
|
Both also change their first argument: instead of the fixture *function*, pass the fixture's *registered name* and let the registry look it up.
|
|
|
|
=== "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(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)]
|
|
```
|
|
|
|
=== "Now (`v5`)"
|
|
|
|
```python
|
|
@fixtures.register(depends_on=["roles"])
|
|
def users():
|
|
admin_role = fixtures.obj(name="roles", attr_name="name", value="admin")
|
|
admin_role_id = fixtures.field(name="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`](https://github.com/d3vyce/fastapi-multiauth).
|
|
|
|
Run `uv add fastapi-multiauth` and replace `from fastapi_toolsets.security import ...` with `from fastapi_multiauth import ...`.
|