
本教程详细介绍了如何使用 Marshmallow 库将模型实例中的简单字符串字段(如 ID)在序列化时转换为嵌套的 JSON 对象。通过定义一个带有 `pre_dump` 钩子的嵌套 Schema,我们能够优雅地将扁平数据结构转换为复杂的嵌套结构,从而满足特定的输出格式要求,确保数据结构清晰且易于维护。
在数据序列化过程中,我们经常需要将模型实例中的某个简单字段(例如一个 ID 字符串)转换为一个嵌套的 JSON 对象,以满足特定的 API 规范或前端展示需求。Marshmallow 提供了强大且灵活的机制来实现这一目标,特别是通过结合 fields.Nested 和 Schema 的 pre_dump 钩子。
假设我们有一个用户模型实例,其中包含一个 parent 属性,它是一个字符串,表示父级用户的 ID。我们希望在序列化该用户实例时,将 parent ID 包装成 { "id": "..." } 这样的嵌套对象,而不是一个简单的字符串。
期望的输出格式:
{
"name": "John",
"parent": {
"id": "123-345"
}
}为了实现上述目标,我们将定义两个 Marshmallow Schema:一个用于包装 ID 的嵌套 Schema,另一个是主用户 Schema。关键在于在嵌套 Schema 中使用 pre_dump 钩子来预处理传入的原始数据。
首先,我们创建一个 IdSchema,它将负责处理 ID 字段。由于原始数据只是一个字符串 ID,而不是一个字典,我们需要在 IdSchema 内部将这个字符串转换为一个字典,以便 fields.String() 能够正确地提取 id 键的值。
from marshmallow import Schema, fields, pre_dump
class 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() 字段正确处理。
接下来,我们定义 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 为空结合上述两个 Schema,我们可以构建一个完整的示例来演示其工作原理。
from marshmallow import Schema, fields, pre_dump
import json
# 1. 定义 IdSchema
class 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. 定义 UserSchema
class 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
}通过这种方法,我们成功地将模型实例中的扁平 ID 字符串在序列化时转换为了所需的嵌套对象结构,同时保持了 Marshmallow Schema 的清晰和专业性。
以上就是Marshmallow 序列化:将模型字段包装为嵌套结构的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号