求助:Marshmallow嵌套序列化时如何携带父字段(Flask+Peewee)
解决方案:给Marshmallow嵌套对象添加父级字段
嘿,我刚好在维护Flask+Marshmallow+Peewee项目时遇到过几乎一模一样的需求!下面分**反序列化(接收客户端数据)和序列化(输出JSON)**两个场景给你讲清楚可行的实现方式:
场景1:反序列化时,将父级字段关联到嵌套子对象
当你接收客户端传来的JSON时,需要把父级的site_id、update_date等字段关联到data数组里的每个子项对应的Peewee模型实例,最方便的方式是用Marshmallow的post_load钩子:
第一步:定义Peewee模型
假设你的模型结构是这样的(父模型和子模型通过外键关联):
from peewee import Model, CharField, DateTimeField, ForeignKeyField, IntegerField import datetime # 父级模型:对应客户端JSON的顶层字段 class SiteUpdate(Model): site_id = CharField() update_date = DateTimeField() updated_by = CharField() crc = CharField() # 子级模型:对应data数组里的每个对象 class SiteData(Model): site_update = ForeignKeyField(SiteUpdate, backref='data') # 外键关联父模型 num = IntegerField() id = CharField() chain = CharField() # 其他字段根据你的需求补充
第二步:编写Marshmallow Schema并添加post_load钩子
在父级Schema的post_load方法里,先创建并保存父模型实例,再遍历data数组给每个子对象绑定父模型:
from marshmallow import Schema, fields, post_load class SiteDataSchema(Schema): num = fields.Integer() id = fields.Str() chain = fields.Str() # 子Schema只需要定义自身的字段,父级关联通过钩子处理 class SiteUpdateSchema(Schema): site_id = fields.Str() # 注意匹配客户端的日期格式,确保能正确解析 update_date = fields.DateTime(format='%Y/%m/%d-%H:%M:%S') updated_by = fields.Str() crc = fields.Str() data = fields.Nested(SiteDataSchema, many=True) @post_load def bind_parent_to_children(self, data, **kwargs): # 1. 先创建并保存父模型实例(必须先保存才能拿到主键用于外键关联) site_update = SiteUpdate( site_id=data['site_id'], update_date=data['update_date'], updated_by=data['updated_by'], crc=data['crc'] ) site_update.save() # 2. 遍历data数组,给每个子对象绑定父模型并保存 for item in data['data']: site_data = SiteData( site_update=site_update, num=item['num'], id=item['id'], chain=item['chain'] # 补充其他子字段 ) site_data.save() return site_update
这样,当你调用SiteUpdateSchema().load(client_json_data)时,就能自动完成父模型和子模型的关联存储。
场景2:序列化时,让嵌套子对象携带父级字段
如果是把数据库中的模型数据序列化成JSON输出,需要让data数组里的每个子对象带上父级的site_id等字段,有两种简单的实现方式:
方式一:在子Schema中用MethodField获取父级字段
直接在子Schema里定义需要携带的父级字段,通过MethodField从关联的父模型中取值:
class SiteDataSchema(Schema): num = fields.Integer() id = fields.Str() chain = fields.Str() # 添加需要携带的父级字段 site_id = fields.Method("get_parent_site_id") update_date = fields.Method("get_parent_update_date") updated_by = fields.Method("get_parent_updated_by") crc = fields.Method("get_parent_crc") def get_parent_site_id(self, obj): return obj.site_update.site_id def get_parent_update_date(self, obj): # 转成客户端需要的日期格式 return obj.site_update.update_date.strftime('%Y/%m/%d-%H:%M:%S') def get_parent_updated_by(self, obj): return obj.site_update.updated_by def get_parent_crc(self, obj): return obj.site_update.crc class SiteUpdateSchema(Schema): site_id = fields.Str() update_date = fields.DateTime(format='%Y/%m/%d-%H:%M:%S') updated_by = fields.Str() crc = fields.Str() data = fields.Nested(SiteDataSchema, many=True)
方式二:在父Schema中用post_dump钩子批量注入父级字段
如果不想修改子Schema,可以在父Schema的post_dump方法里,把父级字段批量添加到每个子对象中:
class SiteDataSchema(Schema): num = fields.Integer() id = fields.Str() chain = fields.Str() # 子Schema保持原有定义即可 class SiteUpdateSchema(Schema): site_id = fields.Str() update_date = fields.DateTime(format='%Y/%m/%d-%H:%M:%S') updated_by = fields.Str() crc = fields.Str() data = fields.Nested(SiteDataSchema, many=True) @post_dump def inject_parent_fields_to_children(self, data, **kwargs): # 提取父级需要传递的字段 parent_fields = { 'site_id': data['site_id'], 'update_date': data['update_date'], 'updated_by': data['updated_by'], 'crc': data['crc'] } # 给每个子对象添加父级字段 for item in data['data']: item.update(parent_fields) return data
两种方式都能实现需求,方式一更灵活(可以单独控制每个父级字段的输出逻辑),方式二更简洁(不需要修改子Schema)。
内容的提问来源于stack exchange,提问作者Alberto
相关产品推荐
相关产品推荐

