【问题标题】:Conditionally changing field properties in Python Marshmallow有条件地更改 Python Marshmallow 中的字段属性
【发布时间】:2020-05-21 01:47:04
【问题描述】:

我正在尝试在扩展 marshmallow.Schema 的类中实现一些逻辑,以便我可以覆盖使用 pre_dump 修饰的方法中某个字段的 load_only 属性的默认值。

默认情况下,load_only=True 但我希望能够在某些情况下将其设置为 False,并认为实现此目的的最佳方法是设置一个可以在预转储方法中检查的上下文值,然后设置 load_only=False 以在序列化期间包含该字段。

为了证明这一点,我有一个 ParentSchema 类和一个 ChildSchema 类。 ParentSchema 有一个名为 children 的字段,其中包含 ChildSchema 对象的列表。

from marshmallow import Schema, fields, post_load, pre_dump
import json


class Child(object):
    def __init__(self, id=None, data=None, parent_id=None):
        self.id = id
        self.data = data
        self.parent_id = parent_id

    def __repr__(self):
        return 'id=' + str(self.id) + ', data=' + str(self.data) + ', parent_id=' + str(self.parent_id)


class ChildSchema(Schema):
    id = fields.Integer(dump_only=True)
    data = fields.String(required=True)
    parent_id = fields.Integer(required=False)

    @post_load
    def make_child(self, data, **kwargs):
        return Child(**data)


class Parent(object):
    def __init__(self, id=None, data=None, children=None):
        self.id = id
        self.data = data
        self.children = children

    def __repr__(self):
        return 'id=' + str(self.id) + ', data=' + str(self.data) + ', children=' + str(self.children)


class ParentSchema(Schema):
    id = fields.Integer(dump_only=True)
    data = fields.String(required=True)
    children = fields.Nested(ChildSchema, many=True, load_only=True)

    @post_load
    def make_parent(self, data, **kwargs):
        return Parent(**data)

    @pre_dump
    def check_context(self, data, **kwargs):
        if 'dump_children' in self.context:
            self.fields['children'].load_only = False
        return data

默认行为应该是不序列化子字段,但是如果在创建 ParentSchema 对象时在上下文字典中提供了键“dump_children”,则 load_only 设置为 False,我希望这会导致子字段为序列化。

parent_data = [
    {
        "data": "parent 1",
        "children": [
            {
                "data": "child 1"
            },
            {
                "data": "child 2"
            }
        ]
    },
    {
        "data": "parent 2",
        "children": [
            {
                "data": "child 3"
            }
        ]
    }
]

parents = ParentSchema(context={'dump_children': True}, many=True).dump(parent_data)
print(json.dumps(parents, indent=2))

我不明白为什么,尽管 load_only 按预期设置为 False,但会产生:

[
  {
    "data": "parent 1"
  },
  {
    "data": "parent 2"
  }
]

代替:

[
  {
    "data": "parent 1",
    "children": [
      {
        "data": "child 1"
      },
      {
        "data": "child 2"
      }
    ]
  },
  {
    "data": "parent 2",
    "children": [
      {
        "data": "child 3"
      }
    ]
  }
]

是否有其他逻辑阻止该字段被序列化?还是有更好的方法来实现我的目标?

【问题讨论】:

    标签: python marshmallow


    【解决方案1】:

    所以我想使用context 做类似的事情来控制被丢弃的内容,我遇到了你的问题。在source code 中的一点挖掘表明,转储过程不会使用load_only=False 动态确定要输出的字段。实际上,在初始化 Schema 实例之后更改字段上的该属性似乎根本没有效果。相反,它使用dump_fields 属性确定要转储的字段,该属性由_init_fields() 方法在初始化时设置。从理论上讲,您可以在更改 childrenload_only 属性后再次调用该方法,但它是一个私有 api 方法,看起来很hacky。

    如果您想有条件地控制输出,我认为您最好使用only/exclude

    parents_only = ParentSchema(exclude=('children',), many=True)
    parents_only.dump(parent_data)
    
    [{'data': 'parent 1'}, {'data': 'parent 2'}]
    
    parents_and_children = ParentSchema(many=True)
    parents_and_children.dump(parent_data)
    
    [{'data': 'parent 1', 'children': [{'data': 'child 1'}, {'data': 'child 2'}]}, {'data': 'parent 2', 'children': [{'data': 'child 3'}]}]
    

    我个人觉得only/exclude 是类级别的参数而不是dump 上的参数有点奇怪,但在this GitHub issuethis one 中有一些讨论。

    除了像这样创建单独的 Schema 实例(您已经在这样做并在上下文中传递)之外,您可能会一起破解某些东西来动态修改 dump_fields,但这似乎不太可取,并且可能会给您自己带来更多问题。

    【讨论】:

    • 感谢您的建议 - 这似乎是一个好方法。最后,我最终使用不同的类来定义不同的模式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-16
    • 1970-01-01
    相关资源
    最近更新 更多