2017-02-14 34 views
1

說我有一個像這樣的模式:在棉花糖架構編程定義字段

class MySchema(Schema): 

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

有沒有一種方法以編程方式添加這些字段的類?

事情是這樣的:

class MyClass(BaseClass): 

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

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

我已經看到了有關動態添加屬性的類實例的帖子,但這是不同的,因爲

  • 我不想打補丁的實例,但一類
  • 棉花糖架構使用自定義元類

在S AME問題可能適用於其他模型定義的庫,例如ODM/ORM(uMongo/MongoEngine,SQL鍊金術,...)

回答

4

所有你需要做的是使用類型()功能與任何屬性,以建立自己的類,你想:

MySchema = type('MySchema', (marshmallow.Schema,), { 
    attr: marshmallow.fields.Float() 
    for attr in FIELDS 
}) 

,你甚至可以有不同的類型的字段存在的:

fields = {} 
fields['foo'] = marshmallow.fields.Float() 
fields['bar'] = marshmallow.fields.String() 
MySchema = type('MySchema', (marshmallow.Schema,), fields) 

或作爲您的定製的基礎:

class MySchema(type('_MySchema', (marshmallow.Schema,), fields)): 
    @marshmallow.post_dump 
    def update_something(self, data): 
     pass 
+0

這是偉大的。它只使用Python而不是特定的棉花糖機制。 –

0

我設法通過繼承默認元類做到這一點:

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 

我做這更通用:

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

我沒有寫

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

因爲那麼所有這些領域將SH是一樣的Field實例。

Field及其ARGS/kwargs必須分別指定:

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

我沒有任何實例的使用情況未能由於多個領域共享同一個實例,但它聽起來並不安全。如果這種預防措施是沒用的,則代碼可以簡化和更具可讀性:

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

顯然,這個答案是特定於棉花糖,並不適用於其他ODM/ORM庫。