【Flask-RESTPlus系列】Part2:响应编组
0x00 内容概览
- 响应编组
- 基本使用
- 重命名属性
- 默认值
- 自定义字段及多值情况
- Url及其他具体字段
- 复杂结构
- 列表字段
- 嵌套字段
- api.model()工厂
- clone实现复制
- api.inherit实现多态
- 自定义字段
- 跳过值为None的字段
- 跳过嵌套字段中的None字段
- 使用JSON Schema定义模型
- 参考链接
0x01 响应编组(Response marshalling)
Flask-RESTPlus提供了一种便捷的方式来控制你在响应中实际渲染的数据,以及在输入载荷(payload)中所期望的数据。利用fields模块,你可以在响应中使用任何对象(ORM模块、自定义类等等)。另外,利用fields也能够实现格式化和过滤响应,这样我们就无需担心暴露内部数据结构的问题。
此外,还有一点好处是,可以很清晰地从你的代码中知道将会渲染什么数据,以及这些数据的格式、结构是怎样的。
1、基本使用
我们可以定义字段的一个字典或者有序字典,其中字典中的key为欲渲染对象的属性名或key,而对应的value则是一个将为该字段格式化并返回值的类。如下面代码所示,该例子中包含三个字段:两个是String类型、一个是格式化为ISO 8601时间字符串(也支持RFC 822)的DateTime类型,如下:
from flask_restplus import Resource, fieldsmodel = api.model('Model', {'name': fields.String,'address': fields.String,'date_updated': fields.DateTime(dt_format='rfc822'), })@api.route('/todo') class Todo(Resource):@api.marshal_with(model, envelope='resource')def get(self, **kwargs):return db_get_todo() # db_get_todo()为某个查询数据的函数
该例子假设你有一个自定义的数据库对象(todo),该对象拥有属性name、address和date_updated。而该对象的其他属性都是私有类型的,且不会在输出中进行渲染。另外,可选参数envelope用来指定封装输出结果。
装饰器marshal_with()接受你的数据对象,并对其按照model格式进行字段过滤。编组(marshalling)可以作用于单个对象、字典或者对象的列表。
注意:marshal_with()是一个很便捷的装饰器,它的作用等价于下面代码:
class Todo(Resource):def get(self, **kwargs):return marshal(db_get_todo(), model), 200
而@api.marshal_with装饰器则增加了swagger文档化功能。
2、重命名属性
大多数情况下,你的共有字段名与你内部的字段名都是不相同的。为了实现这一映射关系的配置,我们可以使用attribute参数:
model = {'name': fields.String(attribute='private_name'),'address': fields.String, }
另外,attribute参数的值也可以指定为lambda表达式或者其他可调用的语句:
model = {'name': fields.String(attribute=lambda x: x._private_name),'address': fields.String, }
此外,还可以利用attribute来访问嵌套的属性:
model = {'name': fields.String(attribute='people_list.0.person_dictionary.name'),'address': fields.String, }
3、默认值
如果因为某个原因,你的数据对象中并不包含字段列表中的某个属性,那么我们就可以为该字段指定一个默认的返回值,从而避免返回None:
model = {'name': fields.String(default='Anonymous User'),'address': fields.String, }
4、自定义字段及多值情况
有时候我们也有自定义格式的需求,此时我们可以让我们的类继承类fields.Raw,并实现format方法。当某个属性存储了多个片段的信息时,这一功能尤其方便。例如,一个bit字段的单个位能够代表不同的值。此时,你可以使用字段来乘以某个属性来来得到多个输出值。
下面示例假设flags属性中的第1个bit用来区分“Normal”和“Urgent”项,而第2个bit则用来区分“Read”和“Unread”。虽然这些项很容易存储在一个bit字段中,但是考虑到输出为了便于人们阅读,将它们分别转换成独立的字符串字段则更加优雅友好:
class UrgentItem(fields.Raw):def format(self, value):return "Urgent" if value & 0x01 else "Normal"class UnreadItem(fields.Raw):def format(self, value):return "Unread" if value & 0x02 else "Read"model = {'name': fields.String,'priority': UrgentItem(attribute='flags'),'status': UnreadItem(attribute='flags'), }
5、Url及其他具体字段
Flask-RESTPlus包含一个特殊字段fields.Url,它会为正被请求的资源生成一个URI。在为响应添加数据对象中不存在的数据时,这一点也是一个不错的示例:
class RandomNumber(fields.Raw):def output(self, key, obj):return random.random()model = {'name': fields.String,# todo_resource是我们调用api.route()时为某个资源指定的端点名'uri': fields.Url('todo_resource'),'random': RandomNumber, }
默认情况下,fields.Url返回的是一个相对于根路径的相对URI。而为了生成包含schema(协议)、主机名和端口号的绝对URI,我们只需在字段声明中传入absolute=True的参数项。为了覆盖默认的schema,我们可以传入schema参数:
model = {'uri': fields.Url('todo_resource', absolute=True)'https_uri': fields.Url('todo_resource', absolute=True, scheme='https') }
6、复杂结构
你可以提供一个扁平的结构,而marshal()则会按照定义的规则将其转换成一个嵌套结构:
>>> from flask_restplus import fields, marshal >>> import json >>> >>> resource_fields = {'name': fields.String} >>> resource_fields['address'] = {} >>> resource_fields['address']['line 1'] = fields.String(attribute='addr1') >>> resource_fields['address']['line 2'] = fields.String(attribute='addr2') >>> resource_fields['address']['city'] = fields.String >>> resource_fields['address']['state'] = fields.String >>> resource_fields['address']['zip'] = fields.String >>> data = {'name': 'bob', 'addr1': '123 fake street', 'addr2': '', 'city': 'New York', 'state': 'NY', 'zip': '10468'} >>> json.dumps(marshal(data, resource_fields)) '{"name": "bob", "address": {"line 1": "123 fake street", "line 2": "", "state": "NY", "zip": "10468", "city": "New York"}}'
注意:上述示例中的address字段其实并不存在于数据对象中,但是任何子字段都能够直接从对象中访问该属性,就像它们并不是嵌套关系一样。
7、列表字段(List Field)
你也可以将字段解组成列表:
>>> from flask_restplus import fields, marshal >>> import json >>> >>> resource_fields = {'name': fields.String, 'first_names': fields.List(fields.String)} >>> data = {'name': 'Bougnazal', 'first_names' : ['Emile', 'Raoul']} >>> json.dumps(marshal(data, resource_fields)) >>> '{"first_names": ["Emile", "Raoul"], "name": "Bougnazal"}'
8、嵌套字段(Nested Field)
既然嵌套字段使用字典可以将一个扁平数据对象转换成一个嵌套响应,那么你也可以使用Nested来将嵌套的数据结构解组,并对其进行适当的渲染:
>>> from flask_restplus import fields, marshal >>> import json >>> >>> address_fields = {} >>> address_fields['line 1'] = fields.String(attribute='addr1') >>> address_fields['line 2'] = fields.String(attribute='addr2') >>> address_fields['city'] = fields.String(attribute='city') >>> address_fields['state'] = fields.String(attribute='state') >>> address_fields['zip'] = fields.String(attribute='zip') >>> >>> resource_fields = {} >>> resource_fields['name'] = fields.String >>> resource_fields['billing_address'] = fields.Nested(address_fields) >>> resource_fields['shipping_address'] = fields.Nested(address_fields) >>> address1 = {'addr1': '123 fake street', 'city': 'New York', 'state': 'NY', 'zip': '10468'} >>> address2 = {'addr1': '555 nowhere', 'city': 'New York', 'state': 'NY', 'zip': '10468'} >>> data = {'name': 'bob', 'billing_address': address1, 'shipping_address': address2} >>> >>> json.dumps(marshal(data, resource_fields)) '{"billing_address": {"line 1": "123 fake street", "line 2": null, "state": "NY", "zip": "10468", "city": "New York"}, "name": "bob", "shipping_address": {"line 1": "555 nowhere", "line 2": null, "state": "NY", "zip": "10468", "city": "New York"}}'
该示例使用两个Nested字段。Nested构造函数接受一个字段组成的字典,然后将其渲染成一个子fields.input对象。Nested构造函数和嵌套字典(上个例子)之间的重要不同点是:属性的上下文环境。在本例中,billing_address是一个复杂的对象,它拥有自己的字段,而传入到嵌套字段中的上下文环境是子对象,而不是原始的data对象。也就是说:data.billing_address.addr1处于该范围,而在前一示例中,data.addr1则是位置属性。记住:Nested和List对象为属性创建了一个新的作用范围。
默认情况下,当子对象为None时,将会为嵌套字段生成一个包含默认值的对象,而不是null值。可以通过传入allow_null参数来修改这一点,查看Nested构造函数以了解更多信息。
使用Nested和List来编组更复杂对象的列表:
user_fields = api.model('User', {'id': fields.Integer,'name': fields.String, })user_list_fields = api.model('UserList', {'users': fields.List(fields.Nested(user_fields)), })
9、api.model()工厂
model()工厂允许我们实例化并注册模型到我们的API和命名空间(Namespace)中。如下所示:
my_fields = api.model('MyModel', {'name': fields.String,'age': fields.Integer(min=0) })# 等价于 my_fields = Model('MyModel', {'name': fields.String,'age': fields.Integer(min=0) }) api.models[my_fields.name] = my_fields
10、clone实现复制
Model.clone()方法使得我们可以实例化一个增强模型,它能够省去我们复制所有字段的麻烦:
parent = Model('Parent', {'name': fields.String })child = parent.clone('Child', {'age': fields.Integer })
Api/Namespace.clone也会将其注册到API。如下:
parent = api.model('Parent', {'name': fields.String })child = api.clone('Child', parent, {'age': fields.Integer })
11、api.inherit实现多态
Model.inherit()方法允许我们以“Swagger”方式扩展模型,并开始解决多态问题:
parent = api.model('Parent', {'name': fields.String,'class': fields.String(discriminator=True) })child = api.inherit('Child', parent, {'extra': fields.String })
Api/Namespace.clone会将parent和child都注册到Swagger模型定义中:
parent = Model('Parent', {'name': fields.String,'class': fields.String(discriminator=True) })child = parent.inherit('Child', {'extra': fields.String })
本例中的class字段只有在其不存在于序列化对象中时,才会以序列化的模型名称进行填充。
Polymorph字段允许你指定Python类和字段规范的映射关系:
mapping = {Child1: child1_fields,Child2: child2_fields, }fields = api.model('Thing', {owner: fields.Polymorph(mapping) })
12、自定义字段
自定义输出字段使得我们可以在无需直接修改内部对象的情况下,进行自定义的输出结果格式化操作。我们只需让类继承Raw,并实现format()方法:
class AllCapsString(fields.Raw):def format(self, value):return value.upper()# 使用示例 fields = {'name': fields.String,'all_caps_name': AllCapsString(attribute='name'), }
也可以使用__schema_format__、__schema_type__和__schema_example__来指定生成的类型和例子:
class MyIntField(fields.Integer):__schema_format__ = 'int64'class MySpecialField(fields.Raw):__schema_type__ = 'some-type'__schema_format__ = 'some-format'class MyVerySpecialField(fields.Raw):__schema_example__ = 'hello, world'
13、跳过值为None的字段
我们可以跳过值为None的字段,而无需将这些字段编组为JSON值null。当你拥有很多值可能会为None的字段,而到底哪个字段的值为None又不可预测时,此时该特性在减小响应大小方面的优势就凸显出来了。
下面例子中,我们将可选参数skip_none设置为True:
>>> from flask_restplus import Model, fields, marshal_with >>> import json >>> model = Model('Model', { ... 'name': fields.String, ... 'address_1': fields.String, ... 'address_2': fields.String ... }) >>> @marshal_with(model, skip_none=True) ... def get(): ... return {'name': 'John', 'address_1': None} ... >>> get() {'name', 'John'}
可以看到,address_1和address_2被marshal_with()跳过了。address_1被跳过是因为它的值为None,而address_2被跳过是因为get()返回的字典中并不包含address_2这个key。
14、跳过嵌套字段中的None字段
如果你的模型使用了fields.Nested,那么你需要传递skip_none=True参数到fields.Nested中,只有这样该Nested字段中的子字段为None时才会被跳过:
>>> from flask_restplus import Model, fields, marshal_with >>> import json >>> model = Model('Model', { ... 'name': fields.String, ... 'location': fields.Nested(location_model, skip_none=True) ... })
15、使用JSON Schema定义模型
我们可以使用JSON Schema(Draft v4)来定义模型:
address = api.schema_model('Address', {'properties': {'road': {'type': 'string'},},'type': 'object' })person = address = api.schema_model('Person', {'required': ['address'],'properties': {'name': {'type': 'string'},'age': {'type': 'integer'},'birthdate': {'type': 'string','format': 'date-time'},'address': {'$ref': '#/definitions/Address',}},'type': 'object' })
0x02 参考链接
- http://flask-restplus.readthedocs.io/en/stable/marshalling.html
转载于:https://www.cnblogs.com/leejack/p/9163568.html
【Flask-RESTPlus系列】Part2:响应编组相关推荐
- LYNC2013部署系列PART2:后端部署
LYNC2013部署系列PART2:后端部署 前言:本篇文章介绍lync后端服务器的部署,先部署好2台后端数据库服务器,分别为lync2013be.contoso.com和lync2013db1.co ...
- Flask开发系列之Web表单
Flask开发系列之Web表单 简单示例 from flask import Flask, request, render_templateapp = Flask(__name__) @app.rou ...
- Flask入门系列(转载)
一.入门系列: Flask入门系列(一)–Hello World 项目开发中,经常要写一些小系统来辅助,比如监控系统,配置系统等等.用传统的Java写,太笨重了,连PHP都嫌麻烦.一直在寻找一个轻量级 ...
- Flask扩展系列(八)–用户会话管理
安装和启用 遵循标准的Flask扩展安装和启用方式,先通过pip来安装扩展: $ pip install Flask-Login 接下来创建扩展对象实例: 1 2 3 4 5 from flask i ...
- ZYNQ开发系列——PS响应PL中断请求
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 ZYNQ开发系列--PS响应PL中断请求 前言 中断的设置 PS部分代码 前言 PS和PL的交互中,还有一个中断没有讲到,在<从 ...
- Flask框架 请求与响应 模板语法
目录 Flask框架 请求与响应 & 模板语法 简单了解Flask框架 Flask 框架 与 Django 框架对比 简单使用Flask提供服务 Flask 中的 Response(响应) F ...
- python flask 设置个性化的响应体 响应头 状态码
目录 需求场景 使用return直接设置特定的响应信息 使用make_response设置特定的响应信息 需求场景 在api设计中,基于restful的设计原则,一个http的响应应该包含执行的响应信 ...
- 【Flask】RESTful的响应处理
1.序列化数据(返回字典格式的数据) Flask-RESTful 提供了marshal工具,用来帮助我们将数据序列化为特定格式的字典数据,以便作为视图的返回值. 使用装饰器的方式 from flask ...
- flask的请求与响应
get请求:先导入request的包,request的参数 args(获取get方法传递过来的变量).base_url.blueprint.charset.cookies.form(获取post传递过 ...
最新文章
- 大脑是怎样和身体交流的?
- jQuery Pagination分页插件的使用
- 通过网络方式安装linux的五种方法
- 移植uboot第八步:裁剪、修改默认参数、分区
- LwIP 之四 超时处理/定时器(timeouts.c/h)
- 养殖者运送猫狗过河问题(面向对象)
- r 数据框选子集_在带有组合框的值列表的下拉列表中显示显示属性的子集
- 牛客21805 字符串编码与解码
- 计算机自动关闭硬盘,你遇到过电脑硬盘自动关闭的问题吗?
- 使用wwise音效引擎的好处
- 获取原始NMEA 0183语句的方法
- 软件项目确立的几个步骤
- 2020年阴历三月初九投资理财~从牛人那里吸取能量,让自己更加强大
- 《动手学深度学习》(PyTorch版)代码注释 - 32 【RNN_with_zero】
- openpyxl中遇到TypeError: ‘generator‘ object is not subscriptable的问题和解决方案
- 创建nfs服务器启动httpd服务但是访问的一直都是欢迎页面
- english_搭配
- python给ppt表格加边框_带你用Python玩转PPT
- 云服务器及其存储性能测试方法
- 逆向微信Mac客户端:微信情话助手初版
热门文章
- oracle sequences优化_Oracle优化(恒生内部常规分享)
- linux配置libpng环境变量_不同操作系统下 jdk 的配置
- maven工程启动时报“Cannot resolve XXX:XXX:xx.xx.xx”错误的问题
- Java并发编程框架Disruptor
- Mysql的limit用法
- C++中size_t和int区别
- Java将数组(Array)用固定分隔符拼接成字符串(String)
- hardmard积 用什么符号表示_表面粗糙度=表面光洁度?数值为什么用0.8、1.6、3.2等表示?...
- nginx 负载均衡 最初级版本
- swoole 服务端heartbeat_check_interval心跳检测 客户端 swoole_timer_tick 发送心跳包 这里只是个demo参考