美文网首页Python接口测试API
Flask RESTful API开发之序列化与反序列化

Flask RESTful API开发之序列化与反序列化

作者: 七月尾巴_葵花 | 来源:发表于2017-06-21 23:55 被阅读130次

    序列化(序列化)与反序列化(Deserialization)是RESTful API开发中绕不开的一环,开发时,序列化与反序列化的功能实现中通常也包含数据校验(验证)相关的业务本文结合我的实践经验,介绍一种Flask RESTful API开发中实现序列化和反序列化的方法,如果想了解更多相关的理论要点,可以参考

    Flask相关功能基础

    python内置数据类型中的dict和list,可以直接序列化为文本,如:

    Dict和list能够直接被序列化为文本,如下:

    def test_list(): 
        data = [{'a':1, 'b':2}, {'c':3, 'd':4}] 
        return jsonify(result=data)
    
    def test_dict1(): 
        data = {'a':1, 'b':2, 'c':3} 
        return jsonify(data)
    
    def test_dict2(): 
       data = {'a':1, 'b':2, 'c':3} 
       return jsonify(**data)
    
    def test_dict3():
        data = {'a':1, 'b':2, 'c':3} 
        return jsonify(result=data)
    

    其中,jsonify的作用是,把dict或list转换为string(类似于json.dumps()),同时,在响应的头中,添加

    content-type =application/json,由于RESTful API通常以json格式作为载体,该方法算是一个捷径。由此,
    对象的序列化,实现只要object -> dict或objects -> list即可。

    反序列化,通常分两步实现:

    1、将请求中的文本数据转换为蟒原生的字典或列表
    2、基于字典为初始化数据,创建类的实例

    反序列化实现的关键是上面第一步,而这一步实现非常容易,如果数据载体为json,调用只需

    request.get_json()方法,即可将传入的文本数据转换为字典或列表。

    Marshmallow

    Marshmallow是一个强大的轮子,很好的实现了

    object -> dict,objects -> list,string -> dict和string -> list。

    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 -> dict,dumps()方法实现 obj -> string,由于Flask能直接序列化dict,所以通常Flask与Marshmallow配合序列化时,用 dump()方法即可。

    from marshmallow import pprint
    
    user = User(name="Monty", email="monty@python.org")
    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'ken@yahoo.com',
        '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': 'ronnie@stones.com'
    }
    schema = UserSchema()
    result = schema.load(user_data)
    result.data  # => <User(name='Ronnie')>
    

    Objects <-> List

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

    user1 = User(name="Mick", email="mick@stones.com")
    user2 = User(name="Keith", email="keith@stones.com")
    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
    

    Partial Loading

    按照RESTful架构风格的要求,更新数据使用HTTP方法中的PUT或PATCH方法,使用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

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

    相关文章

      网友评论

        本文标题:Flask RESTful API开发之序列化与反序列化

        本文链接:https://www.haomeiwen.com/subject/eblxcxtx.html