> 技术文档 > SQLAlchemy 常见问题笔记_sqlalchemy add数据成功 但是报错 unable to serialize unknow

SQLAlchemy 常见问题笔记_sqlalchemy add数据成功 但是报错 unable to serialize unknow


文章目录

    • SQLAlchemy Session对象如何操作数据库
    • SQLAlchemy非序列化对象如何返回
      • 1.问题分析
      • 2.解决方案
        • 方法1:使用 Pydantic 响应模型(推荐)
        • 方法2:手动转换为字典(简单快速)
        • 方法3:使用 SQLAlchemy 的 as_dict 方法(需在模型中添加)
      • 3.完整代码示例
      • 4.最佳实践建议
  • 路由注册

SQLAlchemy Session对象如何操作数据库

  1. 模型元数据关联

    • 当创建模型实例时,SQLAlchemy 知道这个对象对应 __tablename__ 指定的表
    • 所有字段映射到表的列是通过类属性定义的
  2. 会话操作

    • db.add(record):将对象加入会话的\"待处理\"列表

    • db.commit():生成并执行SQL语句:

      sql

      INSERT INTO user_files (id, name, type, ...) VALUES (?, ?, ?, ...)
  3. 表名确定时机

    • 开发时:通过模型的 __tablename__ 静态定义
    • 运行时:SQLAlchemy 通过对象的类信息确定目标表
  4. 为什么不需要显式指定表名

    1. ORM 的核心特性

      • 对象关系映射自动处理表关联
      • 每个模型类隐式绑定到特定表
    2. 会话的通用性

      • 同一个 db 会话可操作多个模型
      • 操作表取决于操作的模型对象类型

总结:

元素 作用 表名确定方式 model模型类 定义表结构 通过 __tablename__ 类属性 record 实例 携带数据 实例所属类决定目标表 db.add() 加入会话 根据实例类型确定表 db.commit() 执行操作 生成对应表的SQL语句

这种设计模式遵循SQLAlchemy的\"Unit of Work\"模式,开发者只需操作Python对象,ORM自动处理表映射和SQL生成。

SQLAlchemy非序列化对象如何返回

1.问题分析

  1. SQLAlchemy 对象不是可序列化类型

    • 开发时创建的模型是 SQLAlchemy 模型实例
    • FastAPI 的 JSONResponse 需要可序列化为 JSON 的 Python 基本类型(dict, list, str等)
  2. __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 内部尝试序列化时:

  1. 检测到 db_user 不是基本类型
  2. 尝试调用 db_user.__dict__
  3. 发现 __dict__ 包含不可序列化的 _sa_instance_state 属性
  4. 最终返回空字典 {} 作为安全处理

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.最佳实践建议

  1. 始终使用 Pydantic 模型定义响应结构
  2. 在数据库操作后立即转换为响应模型
  3. 避免直接返回 ORM 对象,因为它们:
    • 包含数据库会话状态信息
    • 可能导致意外数据暴露
    • 难以控制序列化行为
{ \"code\": 200, \"message\": \"创建成功\", \"data\": { \"id\": \"\", \"username\": \"\" }}

路由注册