首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Marshmallow中以编程方式定义字段

在Marshmallow中以编程方式定义字段
EN

Stack Overflow用户
提问于 2017-02-14 16:29:44
回答 6查看 12.1K关注 0票数 8

假设我有这样一个模式:

代码语言:javascript
复制
class MySchema(Schema):

    field_1 = Float()
    field_2 = Float()
    ...
    field_42 = Float()

有办法以编程方式将这些字段添加到类中吗?

就像这样:

代码语言:javascript
复制
class MyClass(BaseClass):

    FIELDS = ('field_1', 'field_2',..., 'field_42')

    for field in FIELDS:
        setattr(?, field, Float())  # What do I replace this "?" with?

我看过关于向类实例动态添加属性的文章,但是这是不同的,因为

  • 我不想修补一个实例,而是一个类
  • 棉花糖模式使用自定义元类

同样的问题可能也适用于其他模型定义库,如ODM/ORM (uMongo/MongoEngine,SQL Alchemy,.)

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2017-02-15 01:36:23

您所需要做的就是使用type()函数来使用您想要的任何属性构建类:

代码语言:javascript
复制
MySchema = type('MySchema', (marshmallow.Schema,), {
    attr: marshmallow.fields.Float()
    for attr in FIELDS
})

您甚至可以在那里有不同类型的字段:

代码语言:javascript
复制
fields = {}
fields['foo'] = marshmallow.fields.Float()
fields['bar'] = marshmallow.fields.String()
MySchema = type('MySchema', (marshmallow.Schema,), fields)

或者作为自定义的基础:

代码语言:javascript
复制
class MySchema(type('_MySchema', (marshmallow.Schema,), fields)):
    @marshmallow.post_dump
    def update_something(self, data):
        pass
票数 24
EN

Stack Overflow用户

发布于 2017-02-14 16:29:44

通过对默认元类进行子类处理,我成功地做到了这一点:

代码语言:javascript
复制
class MySchemaMeta(SchemaMeta):

    @classmethod
    def get_declared_fields(mcs, klass, cls_fields, inherited_fields, dict_cls):
        fields = super().get_declared_fields(klass, cls_fields, inherited_fields, dict_cls)
        FIELDS = ('field_1', 'field_2',..., 'field_42')
        for field in FIELDS:
            fields.update({field: Float()})
        return fields

class MySchema(Schema, metaclass=MySchemaMeta):

    class Meta:
        strict = True

我把它变得更通用了:

代码语言:javascript
复制
class DynamicSchemaOpts(SchemaOpts):

    def __init__(self, meta):
        super().__init__(meta)
        self.auto_fields = getattr(meta, 'auto_fields', [])


class DynamicSchemaMeta(SchemaMeta):

    @classmethod
    def get_declared_fields(mcs, klass, cls_fields, inherited_fields, dict_cls):

        fields = super().get_declared_fields(klass, cls_fields, inherited_fields, dict_cls)

        for auto_field_list in klass.opts.auto_fields:
            field_names, field = auto_field_list
            field_cls = field['cls']
            field_args = field.get('args', [])
            field_kwargs = field.get('kwargs', {})
            for field_name in field_names:
                fields.update({field_name: field_cls(*field_args, **field_kwargs)})

        return fields


class MySchema(Schema, metaclass=DynamicSchemaMeta):

    OPTIONS_CLASS = DynamicSchemaOpts

    class Meta:
        strict = True
        auto_fields = [
            (FIELDS,
             {'cls': Float}),
        ]

我没有写

代码语言:javascript
复制
class Meta:
    strict = True
    auto_fields = [
        (FIELDS, Float()),
    ]

因为所有这些字段都将共享同一个Field实例。

Field及其args/kwargs必须单独指定:

代码语言:javascript
复制
    class Meta:
        strict = True
        auto_fields = [
            (FIELDS,
             {'cls': Nested,
              'args': (MyEmbeddedSchema),
              'kwargs': {'required': True}
             }),
        ]

由于几个字段共享同一个实例,我没有任何示例用例失败,但它听起来并不安全。如果这种预防措施毫无用处,那么代码就可以简化,并使其更具可读性:

代码语言:javascript
复制
    class Meta:
        strict = True
        auto_fields = [
            (FIELDS, Nested(MyEmbeddedSchema, required=True)),
        ]

显然,这个答案是Marshmallow特有的,不适用于其他ODM/ORM库。

票数 2
EN

Stack Overflow用户

发布于 2019-01-31 19:38:37

类Meta范例允许您指定要序列化的属性。棉花糖将根据属性的类型选择适当的字段类型。

代码语言:javascript
复制
class MySchema(Schema):
    class Meta:
        fields = ('field_1', 'field_2', ..., 'field_42')
    ...

重构:隐式字段创建

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42231334

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档