FastAPI+SQLAlchemy遇UnboundExecutionError错误的解决求助
解决FastAPI + SQLAlchemy的UnboundExecutionError错误
错误原因
你遇到的sqlalchemy.exc.UnboundExecutionError核心问题在于会话管理方式错误:
managers.py中直接导入并实例化sqlalchemy.orm.Session,这个会话没有绑定你在main.py中配置的数据库引擎;- 你在
PlayerManager的__init__中创建了一个会话实例,然后重复用with语句尝试复用它,导致会话被关闭后仍被调用,引发绑定缺失问题。
修复方案
步骤1:修改main.py,传递绑定好的sessionmaker
将main.py中创建的已绑定引擎的Session(sessionmaker实例)传递给PlayerManager,确保manager使用正确的会话工厂:
from fastapi import FastAPI from sqlalchemy import create_engine, URL from sqlalchemy.orm import sessionmaker from models import Base from typing import List from schemas import Player, PlayerInfo from managers import PlayerManager url = URL.create( drivername="postgresql", host="localhost", port=5432, database="casino_player", username="postgres", password="postgres" ) engine = create_engine(url) Base.metadata.create_all(engine) # 已绑定引擎的sessionmaker实例 Session = sessionmaker(bind=engine) app = FastAPI(title="Casino Players", version="1.0.0") # 将sessionmaker传递给PlayerManager player_manager = PlayerManager(Session) # ... 后续路由代码保持不变
步骤2:重构managers.py的会话逻辑
修改PlayerManager,使用传入的sessionmaker创建会话,确保每次操作使用独立的有效会话,同时避免跨会话操作实例:
from typing import List, Type from fastapi import HTTPException from models import CasinoPlayer from schemas import Player, PlayerInfo class PlayerManager: def __init__(self, session_class): # 保存绑定好引擎的sessionmaker类 self.session_class = session_class def add_player(self, player: Player) -> CasinoPlayer: # 每次操作创建新会话 with self.session_class() as session: db_player = CasinoPlayer( name=player.name, win_value=player.win_value, casino_name=player.casino_name ) session.add(db_player) session.commit() # 刷新实例,获取数据库自动生成的id session.refresh(db_player) return db_player def get_player(self, player_id: int) -> Type[CasinoPlayer]: with self.session_class() as session: try: return session.query(CasinoPlayer).filter_by(id=player_id).one() except Exception: raise HTTPException(status_code=404, detail="Гравця з таким ID не знайдено") def get_all_players_info(self) -> List[PlayerInfo]: with self.session_class() as session: players_info_list = [] players_records = session.query(CasinoPlayer).all() for player in players_records: win_value_formatted = f"{player.win_value} $" players_info_list.append(PlayerInfo( id=player.id, name=player.name, win_value=win_value_formatted, casino_name=player.casino_name )) return players_info_list def update_player(self, player_id: int, new_name: str, new_win_value: float, new_casino_name: str): with self.session_class() as session: # 直接在当前会话中查询实例,避免跨会话操作 db_player = session.query(CasinoPlayer).filter_by(id=player_id).one() db_player.name = new_name db_player.win_value = new_win_value db_player.casino_name = new_casino_name session.commit() def delete_player(self, player_id: int): with self.session_class() as session: db_player = session.query(CasinoPlayer).filter_by(id=player_id).one() session.delete(db_player) session.commit() def delete_all_players(self) -> int: with self.session_class() as session: # 使用批量删除,更高效 num_deleted = session.query(CasinoPlayer).delete() session.commit() return num_deleted
关键优化点说明
- 会话绑定正确:现在所有会话都通过
main.py中绑定了引擎的sessionmaker创建,确保会话与数据库引擎关联; - 避免跨会话操作:
update_player和delete_player不再调用get_player(会创建新会话),直接在当前会话中查询实例,避免实例属于不同会话导致的绑定错误; - 批量删除优化:
delete_all_players使用query.delete()批量删除,比循环删除更高效; - 刷新实例:
add_player中添加session.refresh(db_player),确保返回的实例包含数据库自动生成的id字段。
内容的提问来源于stack exchange,提问作者Anton Anpilohov
相关产品推荐
相关产品推荐

