fix(database): 修复 detached 对象延迟加载导致的 greenlet 错误

问题:
- CRUD 返回的对象在 session 关闭后变为 detached 状态
- 访问属性时 SQLAlchemy 尝试延迟加载,但没有 session
- 导致: greenlet_spawn has not been called

根本原因:
- SQLAlchemy 对象在 session 外被访问
- 延迟加载机制尝试在非异步上下文中执行异步操作

修复方案:
1. CRUDBase.get_by(): 在 session 内预加载所有列
2. CRUDBase.get_multi(): 在 session 内预加载所有实例的所有列
3. PersonInfo.get_value(): 添加异常处理,防御性编程

影响:
- 所有通过 CRUD 获取的对象现在都完全加载
- 避免了 detached 对象的延迟加载问题
- 可能略微增加初始查询时间,但避免了运行时错误
This commit is contained in:
Windpicker-owo
2025-11-01 16:22:54 +08:00
parent 19ed3fd048
commit 216c88d138
3 changed files with 36 additions and 13 deletions

View File

@@ -563,10 +563,6 @@ class PersonInfoManager:
logger.debug("get_value获取失败person_id不能为空")
return None
# 使用CRUD进行查询
crud = CRUDBase(PersonInfo)
record = await crud.get_by(person_id=person_id)
model_fields = [column.name for column in PersonInfo.__table__.columns]
if field_name not in model_fields:
@@ -577,11 +573,21 @@ class PersonInfoManager:
logger.debug(f"get_value查询失败字段'{field_name}'未在SQLAlchemy模型和默认配置中定义。")
return None
# 使用CRUD进行查询
crud = CRUDBase(PersonInfo)
record = await crud.get_by(person_id=person_id)
if record:
value = getattr(record, field_name)
if value is not None:
return value
else:
# 在访问属性前确保对象已加载所有数据
# 使用 try-except 捕获可能的延迟加载错误
try:
value = getattr(record, field_name)
if value is not None:
return value
else:
return copy.deepcopy(person_info_default.get(field_name))
except Exception as e:
logger.warning(f"访问字段 {field_name} 失败: {e}, 使用默认值")
return copy.deepcopy(person_info_default.get(field_name))
else:
return copy.deepcopy(person_info_default.get(field_name))