
本教程详细介绍了如何使用 Marshmallow 库将模型实例中的简单字符串字段(如 ID)在序列化时转换为嵌套的 JSON 对象。通过定义一个带有 `pre_dump` 钩子的嵌套 Schema,我们能够优雅地将扁平数据结构转换为复杂的嵌套结构,从而满足特定的输出格式要求,确保数据结构清晰且易于维护。
Marshmallow 序列化:将模型字段包装为嵌套结构
在数据序列化过程中,我们经常需要将模型实例中的某个简单字段(例如一个 ID 字符串)转换为一个嵌套的 JSON 对象,以满足特定的 API 规范或前端展示需求。Marshmallow 提供了强大且灵活的机制来实现这一目标,特别是通过结合 fields.Nested 和 Schema 的 pre_dump 钩子。
场景描述
假设我们有一个用户模型实例,其中包含一个 parent 属性,它是一个字符串,表示父级用户的 ID。我们希望在序列化该用户实例时,将 parent ID 包装成 { “id”: “…” } 这样的嵌套对象,而不是一个简单的字符串。
期望的输出格式:
{ "name": "John", "parent": { "id": "123-345" }}
解决方案:使用 fields.Nested 和 pre_dump
为了实现上述目标,我们将定义两个 Marshmallow Schema:一个用于包装 ID 的嵌套 Schema,另一个是主用户 Schema。关键在于在嵌套 Schema 中使用 pre_dump 钩子来预处理传入的原始数据。
1. 定义嵌套 ID Schema
首先,我们创建一个 IdSchema,它将负责处理 ID 字段。由于原始数据只是一个字符串 ID,而不是一个字典,我们需要在 IdSchema 内部将这个字符串转换为一个字典,以便 fields.String() 能够正确地提取 id 键的值。
豆包大模型
字节跳动自主研发的一系列大型语言模型
834 查看详情
from marshmallow import Schema, fields, pre_dumpclass IdSchema(Schema): """ 用于包装单个 ID 字符串为 {"id": "..."} 格式的 Schema。 """ id = fields.String(required=True) @pre_dump def wrap_id_into_dict(self, data, **kwargs): """ 在序列化之前,将传入的原始 ID 字符串包装成一个字典。 例如,如果 data 是 "123-345",则返回 {"id": "123-345"}。 """ if isinstance(data, str): return {"id": data} # 如果数据已经是字典形式,则直接返回,避免不必要的包装 return data
@pre_dump 解释:@pre_dump 装饰器标记的方法会在 Schema 的字段被处理之前执行。当 UserSchema 中的 parent = fields.Nested(IdSchema) 被调用时,UserSchema 会将模型实例的 parent 属性(例如 “123-345” 字符串)传递给 IdSchema。此时,wrap_id_into_dict 方法会接收到这个字符串,并将其转换成 {“id”: “123-345”} 字典。这个转换后的字典随后会被 IdSchema 的 id = fields.String() 字段正确处理。
2. 定义用户 Schema
接下来,我们定义 UserSchema,其中 parent 字段将使用 fields.Nested(IdSchema) 来引用我们刚刚创建的 IdSchema。
class UserSchema(Schema): """ 用户模型的主 Schema,包含嵌套的 parent ID 字段。 """ name = fields.String(required=True) parent = fields.Nested(IdSchema, allow_none=True) # allow_none 允许 parent 为空
3. 完整示例代码
结合上述两个 Schema,我们可以构建一个完整的示例来演示其工作原理。
from marshmallow import Schema, fields, pre_dumpimport json# 1. 定义 IdSchemaclass IdSchema(Schema): id = fields.String(required=True) @pre_dump def wrap_id_into_dict(self, data, **kwargs): if isinstance(data, str): return {"id": data} return data# 2. 定义 UserSchemaclass UserSchema(Schema): name = fields.String(required=True) parent = fields.Nested(IdSchema, allow_none=True)# 3. 模拟模型实例class UserModel: def __init__(self, name, parent_id=None): self.name = name self.parent = parent_id # parent 是一个字符串 ID# 4. 实例化模型并序列化if __name__ == "__main__": # 示例 1: 包含 parent ID user_with_parent = UserModel(name="John Doe", parent_id="123-345") user_schema = UserSchema() result_with_parent = user_schema.dump(user_with_parent) print("序列化结果 (带父级ID):") print(json.dumps(result_with_parent, indent=2, ensure_ascii=False)) print("n" + "="*30 + "n") # 示例 2: 不包含 parent ID user_without_parent = UserModel(name="Jane Smith") result_without_parent = user_schema.dump(user_without_parent) print("序列化结果 (不带父级ID):") print(json.dumps(result_without_parent, indent=2, ensure_ascii=False))
运行上述代码,将得到以下输出:
序列化结果 (带父级ID):{ "name": "John Doe", "parent": { "id": "123-345" }}==============================序列化结果 (不带父级ID):{ "name": "Jane Smith", "parent": null}
注意事项与总结
@pre_dump 的强大作用: pre_dump 钩子是 Marshmallow 中一个非常强大的特性,它允许你在序列化过程的早期阶段对原始数据进行转换。这对于处理不完全匹配 Schema 结构但需要转换的数据特别有用。数据类型匹配: 当使用 fields.Nested 时,Marshmallow 期望传入的数据是字典或可迭代对象。如果传入的是一个简单类型(如字符串、整数),并且需要将其转换为嵌套对象,那么在嵌套 Schema 中使用 pre_dump 进行预处理是最佳实践。可重用性: IdSchema 可以被其他任何需要将 ID 字符串包装为 {“id”: “…”} 格式的 Schema 重用,提高了代码的模块化和可维护性。allow_none=True: 在 UserSchema 中为 parent 字段设置 allow_none=True 是一个好的习惯,以应对模型实例中 parent 属性可能为 None 的情况,避免序列化错误。
通过这种方法,我们成功地将模型实例中的扁平 ID 字符串在序列化时转换为了所需的嵌套对象结构,同时保持了 Marshmallow Schema 的清晰和专业性。
以上就是Marshmallow 序列化:将模型字段包装为嵌套结构的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/573168.html
微信扫一扫
支付宝扫一扫