I am using a synchronous (SQLite) session in a FastAPI Dependency as shown in the SQLModel Tutorial:
def get_session():
with Session(engine) as session:
yield session
However, when a route "depends" on a Dependency
and a Security
that use get_session
, the session is created twice resulting in errors such as:
sqlalchemy.exc.InvalidRequestError: Object '<User at 0x7f218cbf6a00>' is already attached to session '1' (this is '2')
Here is a reduced example (full working example):
from fastapi import Depends, Security
from fastapi.security import SecurityScopes
from sqlmodel import Session, create_engine, select
def get_session():
with Session(engine) as session:
yield session
async def get_current_user(
security_scopes: SecurityScopes,
token: str = Depends(oauth2_scheme),
session: Session = Depends(get_session)
):
# Token read omitted
user = session.get(User, token_data.user_uuid)
if user is None:
raise credentials_exception
return user
async def get_current_app_user(
current_user: User = Security(get_current_user, scopes=["app"])
):
return current_user
@app.patch("/me", response_model=UserRead)
def update_current_user(
user: UserUpdate,
current_user: User = Depends(get_current_app_user).
session: Session = Depends(get_session),
):
user_data = user.dict(exclude_unset=True)
for key, value in user_data.items():
setattr(current_user, key, value)
# Session and session from current_user are different ⚠️
session.add(current_user)
session.commit()
session.refresh(current_user)
return current_user
How can this be prevented? I would expect a Security to reuse Dependencies as long as use_cache=True
.
Edit: This issue appears only if scopes
of Security
is a non empty list. This is because scopes
are part of the cache_key
:
self.cache_key = (self.call, tuple(sorted(set(self.security_scopes or []))))