You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5.4 KiB
5.4 KiB
SQLAlchemy 2.0 规范合规性修复
日期: 2026-02-09
类型: 技术债务修复
影响范围: Repository 层
问题描述
项目中多个 Repository 文件存在不符合 SQLAlchemy 2.0 规范的代码模式:
- 重复执行查询:同一条 SQL 语句被执行两次
- 错误的结果提取:直接使用
result.all()而不是result.scalars().all()
这些问题会导致:
- 性能问题(重复执行查询)
- 返回错误的数据类型(Row 对象而不是模型对象)
- 不符合 SQLAlchemy 2.0 最佳实践
修复内容
修复的文件(10 个)
server/app/repositories/folder_export_repository.pyserver/app/repositories/folder_share_repository.pyserver/app/repositories/storyboard_resource_repository.pyserver/app/repositories/user_repository.pyserver/app/repositories/screenplay_repository.pyserver/app/repositories/project_repository.pyserver/app/repositories/project_resource_repository.pyserver/app/repositories/project_resource_share_repository.pyserver/app/repositories/project_element_tag_repository.pyserver/app/repositories/base_repository.py
修复模式
问题 1:重复执行查询
# ❌ 修复前
result = await self.session.execute(statement)
result = await self.session.execute(statement) # 重复执行
return list(result.all())
# ✅ 修复后
result = await self.session.execute(statement)
return list(result.scalars().all())
问题 2:错误的结果提取
# ❌ 修复前
result = await self.session.execute(stmt)
return list(result.all()) # 返回 Row 对象
# ✅ 修复后
result = await self.session.execute(stmt)
return list(result.scalars().all()) # 返回模型对象
问题 3:批量查询结果提取
# ❌ 修复前
result = await self.session.execute(stmt)
result = await self.session.execute(stmt)
images = result.all()
# ✅ 修复后
result = await self.session.execute(stmt)
images = result.scalars().all()
修复统计
- 总计修复点:约 30 处
- 涉及方法:
get_user_jobs()- folder_export_repositoryget_expired_jobs()- folder_export_repositoryget_folder_shares()- folder_share_repositoryget_user_shared_folders()- folder_share_repositoryget_images_by_storyboard()- storyboard_resource_repositoryget_videos_by_storyboard()- storyboard_resource_repositoryget_dialogues_by_storyboard()- storyboard_resource_repositoryget_voiceovers_by_dialogue()- storyboard_resource_repositorydeactivate_all_images()- storyboard_resource_repositorydeactivate_all_videos()- storyboard_resource_repositorydeactivate_all_voiceovers()- storyboard_resource_repositoryget_sessions_by_user_id()- user_repositorydelete_sessions_by_user_id()- user_repositoryget_trashed_projects()- project_repositoryget_by_user()- project_repositoryget_by_folder()- project_repositoryget_members()- project_repositoryget_shares()- project_repositoryget_subprojects()- project_repositoryget_versions()- screenplay_repositorycount_by_project_ids()- screenplay_repositorycount_by_parent_project_ids()- screenplay_repositoryget_by_project()- project_resource_repositoryget_by_element_tag_id()- project_resource_repositoryget_by_element()- project_resource_repositoryget_by_target_project()- project_resource_share_repositoryget_by_element()- project_element_tag_repositoryget_by_project()- project_element_tag_repositoryget_all()- base_repository
验证结果
✅ 所有修复的文件通过 getDiagnostics 检查,无语法错误
✅ 使用 grepSearch 验证,确认无重复执行模式残留
✅ 代码符合 SQLAlchemy 2.0 规范
SQLAlchemy 2.0 最佳实践
查询单个模型对象
# ✅ 正确
result = await session.execute(select(Model).where(...))
obj = result.scalar_one_or_none() # 或 scalars().first()
查询多个模型对象
# ✅ 正确
result = await session.execute(select(Model).where(...))
objects = result.scalars().all()
查询聚合结果(count, sum 等)
# ✅ 正确
result = await session.execute(select(func.count(Model.id)))
count = result.scalar_one()
查询多列或 JOIN 结果
# ✅ 正确(返回元组)
result = await session.execute(
select(Model1.id, Model2.name).join(...)
)
rows = result.all() # 返回 [(id1, name1), (id2, name2), ...]
影响评估
- 性能提升:消除了约 30 处重复查询,减少数据库负载
- 数据正确性:确保返回正确的模型对象类型
- 代码质量:符合 SQLAlchemy 2.0 官方规范
- 向后兼容:修复不影响现有功能,仅改进实现方式
后续建议
- 代码审查:在 PR 审查时检查 SQLAlchemy 查询模式
- Linter 规则:考虑添加自定义 Linter 规则检测重复执行
- 文档更新:在技术栈文档中明确 SQLAlchemy 2.0 规范
- 培训:团队成员学习 SQLAlchemy 2.0 最佳实践
参考资料
- SQLAlchemy 2.0 Migration Guide
- SQLAlchemy 2.0 Tutorial
- Jointo Tech Stack Skill -
backend.md