SQLAlchemy 常见问题笔记_sqlalchemy add数据成功 但是报错 unable to serialize unknow
文章目录
- 路由注册
SQLAlchemy Session对象如何操作数据库
-
模型元数据关联:
- 当创建模型实例时,SQLAlchemy 知道这个对象对应
__tablename__
指定的表 - 所有字段映射到表的列是通过类属性定义的
- 当创建模型实例时,SQLAlchemy 知道这个对象对应
-
会话操作:
-
db.add(record)
:将对象加入会话的\"待处理\"列表 -
db.commit()
:生成并执行SQL语句:sql
INSERT INTO user_files (id, name, type, ...) VALUES (?, ?, ?, ...)
-
-
表名确定时机:
- 开发时:通过模型的
__tablename__
静态定义 - 运行时:SQLAlchemy 通过对象的类信息确定目标表
- 开发时:通过模型的
-
为什么不需要显式指定表名:
-
ORM 的核心特性:
- 对象关系映射自动处理表关联
- 每个模型类隐式绑定到特定表
-
会话的通用性:
- 同一个
db
会话可操作多个模型 - 操作表取决于操作的模型对象类型
- 同一个
-
总结:
model
模型类__tablename__
类属性record
实例db.add()
db.commit()
这种设计模式遵循SQLAlchemy的\"Unit of Work\"模式,开发者只需操作Python对象,ORM自动处理表映射和SQL生成。
SQLAlchemy非序列化对象如何返回
1.问题分析
-
SQLAlchemy 对象不是可序列化类型:
- 开发时创建的模型是 SQLAlchemy 模型实例
- FastAPI 的
JSONResponse
需要可序列化为 JSON 的 Python 基本类型(dict, list, str等)
-
__dict__
包含内部属性:- 直接使用
db_user.__dict__
会包含 SQLAlchemy 内部属性(如_sa_instance_state
) - 这些特殊属性无法被 JSON 序列化
- 直接使用
2.解决方案
方法1:使用 Pydantic 响应模型(推荐)
from pydantic import BaseModel# 定义响应模型class UserResponse(BaseModel): id: str username: str@router.get(\"/createUser\")def create_user(db:Session = Depends(get_db)): try: # ...创建用户的代码... db.commit() # 使用 Pydantic 模型转换 response_data = UserResponse( id=db_user.id, username=db_user.username ) return success_response(200, \"创建成功\", response_data.dict()) except Exception as e: # ...
方法2:手动转换为字典(简单快速)
@router.get(\"/createUser\")def create_user(db: Session = Depends(get_db)): try: # ...创建用户的代码... db.commit() # 手动构建可序列化的字典 user_data = { \"id\": db_user.id, \"username\": db_user.username } return success_response(200, \"创建成功\", user_data) except Exception as e: # ...
方法3:使用 SQLAlchemy 的 as_dict 方法(需在模型中添加)
# 在 User 模型类中添加class User(Base): # ... 原有字段定义 ... def as_dict(self): return {c.key: getattr(self, c.key) for c in inspect(self).mapper.column_attrs}# 在路由中使用@router.get(\"/createUser\")def create_user(db: Session = Depends(get_db)): try: # ...创建用户的代码... db.commit() return success_response(200, \"创建成功\", db_user.as_dict()) except Exception as e: # ...
FastAPI 内部尝试序列化时:
- 检测到
db_user
不是基本类型 - 尝试调用
db_user.__dict__
- 发现
__dict__
包含不可序列化的_sa_instance_state
属性 - 最终返回空字典
{}
作为安全处理
3.完整代码示例
@router.get(\"/createUser\")def create_user(db: Session = Depends(get_db)): try: user = UserCreate(username=\"111\") db_user = User() db_user.id = uuid.uuid1().hex db_user.username = user.username db.add(db_user) db.commit() # 解决方案:转换为可序列化的字典 user_data = { \"id\": db_user.id, \"username\": db_user.username, # 添加其他需要返回的字段 } return success_response(200, \"创建成功\", user_data) except Exception as e: db.rollback() return error_response(500, message=\"用户名至少3个字符\")
4.最佳实践建议
- 始终使用 Pydantic 模型定义响应结构
- 在数据库操作后立即转换为响应模型
- 避免直接返回 ORM 对象,因为它们:
- 包含数据库会话状态信息
- 可能导致意外数据暴露
- 难以控制序列化行为
{ \"code\": 200, \"message\": \"创建成功\", \"data\": { \"id\": \"\", \"username\": \"\" }}