代码之家  ›  专栏  ›  技术社区  ›  gmolau

在模式定义后注册post_load hook?

  •  0
  • gmolau  · 技术社区  · 7 年前

    我试图实现一个 deserializes into an object ,其中对象类在架构定义时未知。我想我可以注册一个 post_load appears 那个 后装载 仅适用于类方法。

    • 更新 Schema._hooks
    • 以某种方式在运行时创建绑定方法并注册它。

    既然这两种选择都有点老套,有没有一种官方的方法可以达到同样的效果?

    0 回复  |  直到 7 年前
        1
  •  1
  •   Jérôme    7 年前

    我认为你不需要一个元类。

    使用只需要类的后加载方法定义基本架构。

    class CustomSchema(Schema):
    
        @post_load
        def make_obj(self, data):
            return self.OBJ_CLS(**data)
    

    如果类在导入时是已知的(不是您的用例),这允许您通过提供类来分解实例化。已经很好了。

    class PetSchema(CustomSchema):
    
        OBJ_CLS = Pet
    

    如果类在导入时未知,则可以在之后提供它。

    class PetSchema(CustomSchema):
        pass
    
    
    PetSchema.OBJ_CLS = Pet
    

    如果在实例化之前需要更多的处理,那么可以重写 make_obj

    class PetSchema(CustomSchema):
        def make_obj(self, data):
            data = my_func(data)
            return Pet(**data)
    

    一般来说,这种机制允许您在基本模式中定义钩子。这是一个很好的方法来克服目前棉花糖的限制:事实上 post_load 方法可以按任何顺序执行。定义单个 基类中的方法,每个处理步骤都有一个钩子。(这个人为的例子并不能真正说明这一点。)

    class CustomSchema(Schema):
    
        @post_load
        def post_load_steps(self, data):
            data = self.post_load_step_1(data)
            data = self.post_load_step_2(data)
            data = self.post_load_step_3(data)
            return data
    
        def post_load_step_1(self, data):
            return data
    
        def post_load_step_2(self, data):
            return data
    
        def post_load_step_3(self, data):
            return data
    
    
        2
  •  0
  •   gmolau    7 年前

    from types import MethodType
    
    from marshmallow import Schema, post_load
    from marshmallow.schema import SchemaMeta
    
    class MyCustomSchemaMeta(SchemaMeta):
    
        def __init__(cls, *args, **kwargs):
            super().__init__(*args, **kwargs)
    
            def make_obj(*args, **kwargs):
                raise NotImplementedError
    
            # This post_load call registers the method with the Schema._hooks dict
            cls.make_obj = post_load(make_obj)
    
    class MyCustomSchema(Schema, metaclass=MyCustomSchemaMeta):
        """This is the base class that my schemas inherit."""
    
    # The actual implementation of make_obj (and hence the class to deserialize to)
    # can now be provided at runtime. The post_load call does not affect the schema
    # anymore, but sets some parameters on the method.
    MyCustomSchema.make_obj = MethodType(
        post_load(lambda self, data: MyClass(**data)), MyCustomSchema
    )
    
    推荐文章