首页 / 资讯中心 / 文章详情

FastAPI之Pydantic 与 ORM 模型转换~`Menu(**menus.model_dump())`

FastAPI之Pydantic 与 ORM 模型转换~`Menu(**menus.model_dump())` ★ FEATURED ARTICLE
结论前置new_menu Menu(**menus.model_dump())这行代码的作用是将一个 Pydantic 模型实例menus转换为字典然后以关键字参数的形式传递给Menu类创建一个新的Menu实例。典型场景在 FastAPI SQLAlchemy 项目中接收前端提交的 Pydantic 请求体然后将其转换为 SQLAlchemy ORM 对象并存入数据库。核心拆解menus.model_dump()Pydantic v2 方法将模型转为字典**字典解包运算符将字典的键值对展开为关键字参数Menu(...)调用Menu类的构造函数传入解包后的参数第一部分理解menus.model_dump()1.1 什么是model_dump()model_dump()是 Pydantic v2 中BaseModel的方法用于将模型实例转换为Python 字典。frompydanticimportBaseModelclassMenuCreate(BaseModel):name:strpath:strstatus:int1menusMenuCreate(name首页,path/home,status1)datamenus.model_dump()print(data)# {name: 首页, path: /home, status: 1}1.2 v1 与 v2 的区别版本方法Pydantic v1.dict()Pydantic v2.model_dump()v2 中.dict()仍可用但已弃用推荐使用model_dump()。1.3 常用参数menus.model_dump(exclude_unsetTrue,# 只包含显式设置的字段exclude{id},# 排除某些字段include{name},# 只包含某些字段by_aliasTrue,# 使用字段别名作为键modejson,# 转为 JSON 兼容类型如 datetime → str)示例classMenuCreate(BaseModel):name:strpath:strstatus:int1id:int|NoneNonemenusMenuCreate(name首页,path/home)# 默认包含所有字段包括默认值print(menus.model_dump())# {name: 首页, path: /home, status: 1, id: None}# 只包含显式设置的字段print(menus.model_dump(exclude_unsetTrue))# {name: 首页, path: /home}# 排除 idprint(menus.model_dump(exclude{id}))# {name: 首页, path: /home, status: 1}# JSON 兼容模式print(menus.model_dump(modejson))# {name: 首页, path: /home, status: 1, id: None}第二部分理解**字典解包2.1 基本概念Python 中的**运算符用于将字典解包为关键字参数。data{name:首页,path:/home,status:1}# 不用 **defcreate_menu(name,path,status):print(name,path,status)create_menu(namedata[name],pathdata[path],statusdata[status])# 用 ** 解包create_menu(**data)# 等价于 create_menu(name首页, path/home, status1)2.2 在类构造中的应用classMenu:def__init__(self,name,path,status):self.namename self.pathpath self.statusstatus data{name:首页,path:/home,status:1}menuMenu(**data)print(menu.name)# 首页关键要求字典的键必须与函数/类的参数名完全匹配否则会抛出TypeError。data{title:首页,path:/home}# 键名不匹配Menu(**data)# TypeError: __init__() got an unexpected keyword argument title第三部分理解Menu(**...)假设Menu是 SQLAlchemy 的 ORM 模型类fromsqlalchemyimportColumn,Integer,String,DateTimefromsqlalchemy.ormimportdeclarative_basefromdatetimeimportdatetime Basedeclarative_base()classMenu(Base):__tablename__menusidColumn(Integer,primary_keyTrue,autoincrementTrue)nameColumn(String(50),nullableFalse)pathColumn(String(200),nullableFalse)statusColumn(Integer,default1)created_atColumn(DateTime,defaultdatetime.utcnow)updated_atColumn(DateTime,defaultdatetime.utcnow,onupdatedatetime.utcnow)def__repr__(self):returnfMenu(id{self.id}, name{self.name})SQLAlchemy 的模型类允许通过关键字参数初始化menuMenu(name首页,path/home,status1)因此Menu(**menus.model_dump())就是将 Pydantic 模型的字典解包后传给 SQLAlchemy 模型构造函数。第四部分完整示例4.1 定义模型frompydanticimportBaseModel,ConfigDictfromsqlalchemyimportColumn,Integer,String,DateTimefromsqlalchemy.ormimportdeclarative_basefromdatetimeimportdatetime# SQLAlchemy ORM 模型Basedeclarative_base()classMenu(Base):__tablename__menusidColumn(Integer,primary_keyTrue,autoincrementTrue)nameColumn(String(50),nullableFalse)pathColumn(String(200),nullableFalse)statusColumn(Integer,default1)created_atColumn(DateTime,defaultdatetime.utcnow)updated_atColumn(DateTime,defaultdatetime.utcnow,onupdatedatetime.utcnow)# Pydantic 请求模型classMenuCreate(BaseModel):name:strpath:strstatus:int1# Pydantic 响应模型classMenuResponse(BaseModel):model_configConfigDict(from_attributesTrue)id:intname:strpath:strstatus:intcreated_at:datetime updated_at:datetime4.2 在 FastAPI 中使用fromfastapiimportFastAPI,Dependsfromsqlalchemy.ormimportSession appFastAPI()defget_db():# 省略数据库会话创建...app.post(/menus,response_modelMenuResponse)defcreate_menu(menus:MenuCreate,db:SessionDepends(get_db)):# 核心转换代码new_menuMenu(**menus.model_dump())db.add(new_menu)db.commit()db.refresh(new_menu)returnnew_menu流程FastAPI 将请求体解析为MenuCreate实例menusmenus.model_dump()转为字典{name: 首页, path: /home, status: 1}Menu(**...)创建 SQLAlchemy 对象存入数据库并返回4.3 手动调用示例# 模拟前端提交的数据menusMenuCreate(name首页,path/home,status1)# 转换为 ORM 对象new_menuMenu(**menus.model_dump())print(new_menu.name)# 首页print(new_menu.path)# /homeprint(new_menu.status)# 1print(new_menu.id)# None尚未入库第五部分注意事项与常见问题5.1 字段名必须匹配Pydantic 模型的字段名必须与 SQLAlchemy 模型的属性名一致否则解包会失败。解决方案使用别名或手动映射。classMenuCreate(BaseModel):model_configConfigDict(populate_by_nameTrue)menu_name:strField(aliasname)menu_path:strField(aliaspath)# 使用别名解包datamenus.model_dump(by_aliasTrue)new_menuMenu(**data)5.2 排除不需要的字段Pydantic 模型可能包含 ORM 模型没有的字段如id、created_at直接解包会报错。# 排除 id 和时间戳datamenus.model_dump(exclude{id,created_at,updated_at})new_menuMenu(**data)5.3 只包含用户显式设置的字段使用exclude_unsetTrue避免将默认值覆盖数据库默认值datamenus.model_dump(exclude_unsetTrue)new_menuMenu(**data)5.4 类型转换问题model_dump()默认返回 Python 原生类型但modejson会转为 JSON 兼容类型如datetime→ 字符串。如果 SQLAlchemy 需要datetime对象不要用modejson。# 正确保留 datetime 对象datamenus.model_dump()# 错误datetime 变成字符串可能导致数据库错误datamenus.model_dump(modejson)5.5 嵌套模型如果 Pydantic 模型包含嵌套模型model_dump()会递归转为字典。但 SQLAlchemy 模型通常需要关联对象不能直接接收字典。classMenuCreate(BaseModel):name:strchildren:list[MenuCreate][]# 嵌套字典不能直接用于 SQLAlchemy 关系# 需要手动处理5.6 别名与populate_by_name如果 Pydantic 模型使用了别名model_dump()默认使用字段名。若 SQLAlchemy 模型期望别名字段需by_aliasTrue。第六部分替代方案与最佳实践6.1 使用model_validate反向转换如果Menu也是一个 Pydantic 模型可以直接new_menuMenu.model_validate(menus)但 SQLAlchemy 模型不是 Pydantic 模型所以不适用。6.2 使用from_ormv1或model_validatev2反向从 ORM 对象创建 Pydantic 模型# v2menu_responseMenuResponse.model_validate(menu_orm)# v1menu_responseMenuResponse.from_orm(menu_orm)6.3 封装转换函数defpydantic_to_sqlalchemy(pydantic_obj:BaseModel,sqlalchemy_class):datapydantic_obj.model_dump(exclude_unsetTrue)returnsqlalchemy_class(**data)new_menupydantic_to_sqlalchemy(menus,Menu)6.4 使用dict()兼容 v1如果项目还在 Pydantic v1new_menuMenu(**menus.dict())6.5 处理额外字段classMenuCreate(BaseModel):model_configConfigDict(extraforbid)# 禁止额外字段name:strpath:str第七部分常见错误与调试错误 1TypeError: __init__() got an unexpected keyword argument原因Pydantic 模型有 ORM 模型没有的字段。解决使用exclude排除多余字段。datamenus.model_dump(exclude{id,created_at})错误 2AttributeError: MenuCreate object has no attribute model_dump原因使用的是 Pydantic v1。解决改用.dict()或升级到 v2。错误 3字段值为None导致数据库非空约束失败原因Pydantic 模型默认值None被解包传入。解决使用exclude_unsetTrue或exclude_noneTrue。datamenus.model_dump(exclude_noneTrue)错误 4datetime类型错误原因使用了modejson导致datetime变成字符串。解决去掉modejson。总结部分作用menus.model_dump()Pydantic 模型 → Python 字典**字典 → 关键字参数Menu(...)调用 ORM 类构造函数创建实例整体Pydantic 请求模型 → SQLAlchemy ORM 对象最佳实践使用exclude_unsetTrue避免覆盖默认值使用exclude排除 ORM 不需要的字段确保字段名匹配必要时使用别名不要使用modejson除非 ORM 需要 JSON 类型封装转换函数以提高复用性这行代码虽然简短但涵盖了 Pydantic 与 SQLAlchemy 集成的核心模式是 FastAPI 项目中最常见的转换之一。
阅读完成 · 觉得有帮助?
咨询建站