美文网首页
Flask-RESTful 输出字段

Flask-RESTful 输出字段

作者: SingleDiego | 来源:发表于2020-05-27 15:25 被阅读0次

    参考资料:https://flask-restful.readthedocs.io/en/latest/fields.html




    Flask-RESTful 提供了一个简单的方式来控制在你的响应中实际呈现什么数据。使用 fields 模块,你可以使用在你的资源里的任意对象(ORM 模型、定制的类等)并且 fields 让你格式化和过滤响应,因此您不必担心暴露内部数据结构。




    基本用法

    你可以定义一个字典或者 fieldsOrderedDict 类型,OrderedDict 类型是指键名是要呈现的对象的属性或键的名称,键值是一个类,该类格式化和返回的该字段的值。

    这个例子有三个字段,两个是字符串(Strings)以及一个是日期时间(DateTime),格式为 RFC 822 日期字符串(同样也支持 ISO 8601

    from flask_restful import Resource, fields, marshal_with
    
    resource_fields = {
        'name': fields.String,
        'address': fields.String,
        'date_updated': fields.DateTime(dt_format='rfc822'),
    }
    
    class Todo(Resource):
        @marshal_with(resource_fields, envelope='resource')
        def get(self, **kwargs):
            return db_get_todo()  # Some function that queries the db
    

    这个例子假设你有一个自定义的数据库对象(todo),其属性为 nameaddressdate_updated。我们可以自己选择传递和读取哪些对象属性。

    装饰器 marshal_with 指定按照哪个字典来读取参数,envelope 关键字设定了数据的包装后的命名。

    下面演示一个完整例子:

    todos = {'1': {'name': 'tom', 'address': '20th street '}}
    
    resource_fields = {
        'name': fields.String,
        'address': fields.String,
    }
    
    class Todo(Resource):
    
        @marshal_with(resource_fields, envelope='resource')
        def get(self, id):
            # 这里实际操作中可写成用 ORM 模块获取对象实例的代码
            return todos[id]
    
    api.add_resource(Todo, '/<string:id>')
    

    以上代码定义的 API:http://127.0.0.1:5000/1,其结果是:

    {"resource": {"name": "tom", "address": "20th street "}}
    

    也可以不使用装饰器 marshal_with 而改用 marshal 方法:

    from flask_restful import Resource, fields, marshal
    
    todos = {'1': {'name': 'tom', 'address': '20th street '}}
    
    resource_fields = {
        'name': fields.String,
        'address': fields.String,
    }
    
    class Todo(Resource):
    
        def get(self, id):
            # 这里实际操作中可写成用 ORM 模块获取对象实例的代码
            data = todos[id]
            return marshal(data, resource_fields, envelope='todo')
    
    api.add_resource(Todo, '/<string:id>')
    

    envelope 在这里是可选的参数,API 的结果:

    { "todo": {"name": "tom", "address": "20th street "}}
    




    属性重命名

    如果想在 API 展示的字段名和对象的属性名不一致,可使用 attribute 关键字来重命名。

    todos = {'1': {'user_name': 'tom', 'address': '20th street '}}
    
    resource_fields = {
        'name': fields.String(attribute='user_name'),
        'address': fields.String,
    }
    
    class Todo(Resource):
    
        @marshal_with(resource_fields, envelope='todos')
        def get(self, id):
            return todos[id]
    
    api.add_resource(Todo, '/<string:id>')
    

    以上代码定义的 API:http://127.0.0.1:5000/1,其结果是:

    {"todos": {"address": "20th street ", "name": "tom"}}
    

    attribute 关键字也可以使用 lambda 方法来定义更复杂的属性名。

    fields = {
        'name': fields.String(attribute=lambda x: x._private_name),
        'address': fields.String,
    }
    

    嵌套属性的访问方法:

    fields = {
        'name': fields.String(attribute='people_list.0.person_dictionary.name'),
        'address': fields.String,
    }
    




    默认值

    如果对象实例某个属性不存在,会返回 null

    todos = {'1': {'name': 'tom', 'address': '20th street '}}
    
    resource_fields = {
        'name': fields.String(),
        'address': fields.String,
        'age': fields.String,
    }
    
    class Todo(Resource):
    
        @marshal_with(resource_fields, envelope='todos')
        def get(self, id):
            return todos[id]
    
    api.add_resource(Todo, '/<string:id>')
    

    API 结果:

    {"todos": {"address": "20th street ", "age": null, "name": "tom"}}
    

    可以用 default 关键字来设置默认值来代替返回空值:

    fields = {
        'name': fields.String(default='Anonymous User'),
        'address': fields.String,
    }
    




    字段格式化 / 自定义

    当我们需要把对象实例的属性值格式化再输出时,可以继承 fields.Raw 类并且实现格式化函数。

    todos = {'1': {
        'name': 'tom', 
        'address': '20th street',
        'age': 12
        }}
    
    class FormatItem(fields.Raw):
        # 自定义格式化函数
        def format(self, value):
            return "adult" if value > 18 else "child"
    
    resource_fields = {
        'name': fields.String,
        'address': fields.String,
        'generation': FormatItem(attribute='age'),
    }
    
    class Todo(Resource):
    
        @marshal_with(resource_fields, envelope='todos')
        def get(self, id):
            return todos[id]
    
    api.add_resource(Todo, '/<string:id>')
    

    API 结果:

    {"todos": {"name": "tom", "generation": "child", "address": "20th street"}}
    




    URL

    Flask-RESTful 包括一个特殊的字段,field.Url,他根据其他 Resource 类的 endpoint 生成一个 URL。

    user_fields = {
        'name': fields.String,
    }
    
    class User(Resource):
        @marshal_with(user_fields, envelope='user')
        def get(self):
            return {'name': 'tom'}
    api.add_resource(User, '/user', endpoint='/user')
    
    
    post_fields = {
        'content': fields.String,
        # todo_resource is the endpoint name when you called api.add_resource()
        'user': fields.Url('/user'),
    }
    
    class Post(Resource):
        @marshal_with(post_fields, envelope='post')
        def get(self):
            return {'content': '2333'}
    api.add_resource(Post, '/post')
    

    生成的 API http://127.0.0.1:5000/post 结果:

    {
        "post": {
            "content": "2333",
            "user": "/user"
        }
    }
    

    下面我们使用 fields.Urlendpoint 来处理更复杂的情况,postuser 都要带上自己的 id

    users = {
        '1': {'name': 'tom'}
    }
    
    posts = {
        '1': {'content': '2333', 'author_id': '1'}
    }
    
    
    user_fields = {
        'name': fields.String,
    }
    
    class User(Resource):
        @marshal_with(user_fields, envelope='user')
        def get(self, user_id):
            user = users.get(user_id)
            return user
    api.add_resource(User, '/user/<string:user_id>', endpoint='/user')
    
    
    post_fields = {
        'content': fields.String,
        # 根据 Resource 类的 endpoint 来匹配
        'user': fields.Url('/user'),
    }
    
    class Post(Resource):
        @marshal_with(post_fields, envelope='post')
        def get(self, post_id):
            post = posts.get(post_id)
            post['user'] = users.get(post.get('author_id'))
            # 因为 user 的 url 设定了 user_id 参数,这里也要带上 user_id
            post['user_id'] = post.get('author_id')
            return post
    api.add_resource(Post, '/post/<string:post_id>')
    

    http://127.0.0.1:5000/post/1 的输出结果:

    {
        "post": {
            "content": "2333",
            "user": "/user/1"
        }
    }
    




    列表字段

    使用 fields.List 字段返回列表:

    todos = {'1': {'name': 'tom', 'address': ['China', 'Beijing', 'ChaoYang']}}
    
    resource_fields = {
        'name': fields.String,
        'address': fields.List(fields.String),
    }
    
    class Todo(Resource):
        @marshal_with(resource_fields, envelope='user')
        def get(self, id):
            # 这里实际操作中可写成用 ORM 模块获取对象实例的代码
            return todos[id]
    api.add_resource(Todo, '/<string:id>')
    

    结果:

    {
        "user": {
            "name": "tom",
            "address": ["China", "Beijing",  "ChaoYang"]
        }
    }
    




    嵌套字段

    如果 user 对象的一个属性 address 也是另一个对象, address 本身也有自己的属性,这时就需要用到嵌套字段,使用 fields.Nested 来设置。

    users = {'1': {'name': 'tom', 'address_id': '1'}}
    addresses = {'1': {'country': 'china', 'city': 'Beijing', 'district': 'ChaoYang'}}
    
    address_fields = {
        'country' : fields.String,
        'city' : fields.String,
        'district' : fields.String,
    }
    
    user_fields = {
        'name': fields.String,
        'address': fields.Nested(address_fields),
    }
    
    class User(Resource):
    
        @marshal_with(user_fields, envelope='user')
        def get(self, id):
            # 这里实际操作中可写成用 ORM 模块获取对象实例的代码
            user = users[id]
            user['address'] = addresses[user.get('address_id')]
            return user
    
    api.add_resource(User, '/<string:id>')
    

    结果:

    {
        "user": {
            "address": {
                "city": "Beijing",
                "district": "ChaoYang",
                "country": "china"
            },
            "name": "tom"
        }
    }
    

    相关文章

      网友评论

          本文标题:Flask-RESTful 输出字段

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