我正在尝试实现deserializes into an object的Schema,其中在Schema定义时不知道对象类。我本来可以在运行时注册post_load
函数,但是appears post_load
仅适用于类方法。
似乎我可以通过以下任何一种方法使其工作:
Schema._hooks
由于这两个选项都有些破绽,是否有正式的方法可以达到相同的结果?
答案 0 :(得分:1)
我认为您不需要元类。
使用仅需要类的后加载方法定义基本架构。
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
方法可以以任何顺序执行。在基类中定义单个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
答案 1 :(得分:0)
只要其他人需要它,我就用一个自定义的元类解决了它,该类预注册了一个post_load函数,该函数的实际实现可以在运行时提供:
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
)