python序列化框架Marshmallow

66696-00581b560ef20d72.png
image

序列化(Serialization)与反序列化(Deserialization)是RESTful API 开发中绕不开的一环,开发时,序列化与反序列化的功能实现中通常也会包含数据校验(Validation)相关的业务逻辑。 本文介绍一个强大的序列化处理框架Marshmallow。

Marshmallow 是一个强大的轮子,很好的实现了 object -> dictobjects -> liststring -> dictstring -> list

Marshmallow is an ORM/ODM/framework-agnostic library for converting complex datatypes, such as objects, to and from native Python datatypes.

-- From marshmallow官方文档

Marshmallow的使用,将从下面几个方面展开,在开始之前,首先需要一个用于序列化和反序列化的类,我直接与marshmallow官方文档保持一致了:

class User(object):
    def __init__(self, name, email):
        self.name = name
        self.email = email
        self.created_at = dt.datetime.now()

Schema

要对一个类(记为Class_A,以便表达)进行序列化和反序列化,首先要创建一个与之对应的类(记Class_A'),负责实现Class_A的序列化、序列化和数据校验等,Class_A'就是schema,即:

Schema是序列化功能的载体,每个需要被序列化或反序列化的类,都要设计一个相应的Schema,以实现相关功能。Schema中的字段,是被序列化的类的映射,如:

class UserSchema(Schema):
    name = fields.Str()
    email = fields.Email()
    created_at = fields.DateTime()

创建schema时,schema中的字段必须与类的成员命名一致,不一致的字段无法被序列化和反序列化。

序列化

序列化使用schema中的dump()dumps()方法,其中,dump() 方法实现obj -> dictdumps()方法实现 obj -> string,由于Flask能直接序列化dict,所以通常Flask与Marshmallow配合序列化时,用 dump()方法即可。

from marshmallow import pprint

user = User(name="Monty", email="[email protected]")
schema = UserSchema()
result = schema.dump(user)
pprint(result.data)

json_result = schema.dumps(user)
pprint(json_result.data)

反序列化

反序列化基于schema中的load()loads()方法,默认情况下,load()方法将一个传入的dict,结合schema的约定,再转换为一个dict,而loads()方法的传入参数是json格式的string,同样将传入数据转换为符合规范的dict。由于调用load()loads()方法时,会执行下面提到的数据校验,所以在开发RESTful API时,对传入数据执行load()loads()方法是必要的。load()方法使用如下:

from pprint import pprint

user_data = {
    'created_at': '2014-08-11T05:26:03.869245',
    'email': u'[email protected]',
    'name': u'Ken'
}
schema = UserSchema()
result = schema.load(user_data)
pprint(result.data)

对反序列化而言,将传入的dict变成object更加有意义。在Marshmallow中,dict -> object的方法需要自己实现,然后在该方法前面加上一个decoration:post_load即可,即:

from marshmallow import Schema, fields, post_load

class UserSchema(Schema):
    name = fields.Str()
    email = fields.Email()
    created_at = fields.DateTime()

    @post_load
    def make_user(self, data):
        return User(**data)

这样每次调用load()方法时,会按照make_user的逻辑,返回一个User类对象。

user_data = {
    'name': 'Ronnie',
    'email': '[email protected]'
}
schema = UserSchema()
result = schema.load(user_data)
result.data  # => <User(name='Ronnie')>

Objects <-> List

上面的序列化和反序列化,是针对一个object而言的,对于objects的处理,只需在schema中增加一个参数:many=True,即:

user1 = User(name="Mick", email="[email protected]")
user2 = User(name="Keith", email="[email protected]")
users = [user1, user2]

# option 1:
schema = UserSchema(many=True)
result = schema.dump(users)

# Option 2:
schema = UserSchema()
result = schema.dump(users, many=True)
result.data

Validation

Marshmallow中的Validation功能用于校验客户端传入的数据是否规范,通常用于创建和修改数据。

Validation可分为 field level validationschema level validation,创建schema时,实现必要Validation是必须的,由于详细阐述占用的篇幅会比较长,这部分内容请大家直接查看官方文档:

Partial Loading

按照RESTful架构风格的要求,更新数据使用HTTP方法中的PUTPATCH方法,使用PUT方法时,需要把完整的数据全部传给服务器,使用PATCH方法时,只需把需要改动的部分数据传给服务器即可。因此,当使用PATCH方法时,传入数据存在无法通过Marshmallow 数据校验的风险,为了避免这种情况,需要借助Partial Loading功能。

实现Partial Loadig只要在schema中增加一个partial参数即可:

class UserSchema(Schema):
    name = fields.String(required=True)
    age = fields.Integer(required=True)

data, errors = UserSchema().load({'age': 42}, partial=True)
# OR UserSchema(partial=True).load({'age': 42})
data, errors  # => ({'age': 42}, {})

Context

关于Marshmallow,最后想提的一点是,context

The context attribute of a Schema is a general-purpose store for extra information that may be needed for (de)serialization. It may be used in both Schema and Field methods.

Context是很用的,比如,更新数据时,更新的是一个数据库中已有的对象,反序列化操作如果能基于该对象来反序列化就再好不过了,实现这个小需求一种可行方案是,把该对象放到context中,schema中针对该对象实现相关业务逻辑。

参考

https://marshmallow.readthedocs.io/

猜你喜欢

转载自blog.csdn.net/weixin_34372728/article/details/87169535
今日推荐