Define fields programmatically in Marshmallow Schema

后端 未结 4 531
不思量自难忘°
不思量自难忘° 2021-02-09 05:27

Say I have a Schema like this:

class MySchema(Schema):

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

Is there a w

4条回答
  •  太阳男子
    2021-02-09 05:47

    I managed to do it by subclassing the default metaclass:

    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({fluid: Float()})
            return fields
    
    class MySchema(Schema, metaclass=MySchemaMeta):
    
        class Meta:
            strict = True
    

    I made this more generic:

    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}),
            ]
    

    I didn't write

    class Meta:
        strict = True
        auto_fields = [
            (FIELDS, Float()),
        ]
    

    because then all those fields would share the same Field instance.

    The Field and its args/kwargs must be specified separately:

        class Meta:
            strict = True
            auto_fields = [
                (FIELDS,
                 {'cls': Nested,
                  'args': (MyEmbeddedSchema),
                  'kwargs': {'required': True}
                 }),
            ]
    

    I don't have any example use case failing due to several fields sharing the same instance, but it doesn't sound safe. If this precaution is useless then the code could be simplified and made more readable:

        class Meta:
            strict = True
            auto_fields = [
                (FIELDS, Nested(MyEmbeddedSchema, required=True)),
            ]
    

    Obviously, this answer is specific to Marshmallow and does not apply to other ODM/ORM libraries.

提交回复
热议问题