1、Restful规范返回值
对于一个类视图,可以指定好一些字段做标准化用于返回。以后使用ORM模型或者自定义模型的时候,他会自动的获取模型中的相应的字段,生成json格式数据,然后再返回给客户端。
使用方法:
- 导入 flask_restful.marshal_with 装饰器
- 定义一个字典变量来指定需要返回的标准化字段,以及该字段的数据类型
在请求方法中,返回自定义对象的时候,flask_restful会自动的读取对象模型上的所有属性。组装成一个符合标准化参数的json格式字符串返回给客户端
示例代码:
from flask import Flask
from flask_restful import Api, Resource, fields, marshal_with
app = Flask(__name__)
api = Api(app)
class News:
def __init__(self, code, msg, state, content):
self.code = code
self.msg = msg
self.state = state
self.content = content
class NewsView(Resource):
resouce_fields = {
'code': fields.Integer,
'msg': fields.String,
'state': fields.String
}
@marshal_with(resouce_fields)
def get(self):
return {'code': 200, 'msg': '访问成功!', 'state': 'OK'}
@marshal_with(resouce_fields)
def post(self):
return {'msg': '注册成功!'}
@marshal_with(resouce_fields)
def put(self):
# 在返回对象时,会自动在对象中获取与约定好的字段,并获取封装成json。
news = News(404, '注册成功', 'OK', 'dgw')
return news
api.add_resource(NewsView, '/news/')
if __name__ == '__main__':
app.run(debug=True)
2、序列化数据
Flask-RESTful 提供了marshal工具,用来帮助我们将数据序列化为特定格式的字典数据,以便作为视图的返回值。
from flask_restful import Resource, fields, marshal_with
resource_fields = {
'name': fields.String,
'address': fields.String,
'user_id': fields.Integer
}
class Todo(Resource):
@marshal_with(resource_fields, envelope='resource')
def get(self, **kwargs):
return db_get_todo()
也可以不使用装饰器的方式
class Todo(Resource):
def get(self, **kwargs):
data = db_get_todo()
return marshal(data, resource_fields)
示例代码1:
from flask import Flask
from flask_restful import fields, Resource, marshal, marshal_with, Api
app = Flask(__name__)
api = Api(app)
# 用来模拟要返回的数据对象的类
class User(object):
def __init__(self, user_id, name, age):
self.user_id = user_id
self.name = name
self.age = age
resoure_fields = {
'user_id': fields.Integer,
'name': fields.String
}
class Demo1Resource(Resource):
@marshal_with(resoure_fields, envelope='data1')
def get(self):
user = User(1, 'dgw', 12)
return user
class Demo2Resource(Resource):
def get(self):
user = User(1, 'dgw', 12)
return marshal(user, resoure_fields, envelope='data2')
api.add_resource(Demo1Resource, '/demo1')
api.add_resource(Demo2Resource, '/demo2')
if __name__ == '__main__':
app.run(debug=True)
运行效果:
测试代码2:
运行结果:
示例代码3:
from flask import Flask
from flask_restful import reqparse, Api, Resource, fields, marshal_with
app = Flask(__name__)
api = Api(app)
# 1. 关于参数解析的部分
# 一组虚拟的数据
TODOS = {
'todo1': {'task': 1},
'todo2': {'task': 2},
'todo3': {'task': 3},
}
# 定义允许的参数为task,类型为int,以及错误时的提示
parser = reqparse.RequestParser()
parser.add_argument('task', type=int, help='Please set a int task content!')
# 真正处理请求的地方
class TodoList(Resource):
def get(self):
return TODOS, 200, {'Etag': 'some-opaque-string'}
def post(self):
args = parser.parse_args()
print(max(TODOS.keys()), type(max(TODOS.keys())))
todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1
todo_id = 'todo%i' % todo_id
TODOS[todo_id] = {'task': args['task']}
return TODOS[todo_id], 201
# 实际定义路由的地方
api.add_resource(TodoList, '/todos', '/all_tasks')
# 2. 关于响应域的部分
# ORM的数据模型
class TodoDao(object):
def __init__(self, todo_id, task):
self.todo_id = todo_id
self.task = task
# 这个域不会被返回
self.status = 'active'
# marshal-蒙板,
resource_fields = {
'task': fields.String,
'url': fields.Url('todo_ep')
}
# 真正处理请求的地方
class Todo(Resource):
# 蒙板 把多余的参数蒙住,只返回resource_fields中声明的键值对
@marshal_with(resource_fields)
def get(self, todo_id):
return TodoDao(todo_id=todo_id, task='Remember the milk'), 200
# 实际定义路由的地方
api.add_resource(Todo, '/todos/<todo_id>', endpoint='todo_ep') # 类Todo中只写了get方法
if __name__ == '__main__':
app.run(debug=True)
运行结果:
get功能:
post功能:
3、Restful规范返回值-参数设置
3.1 设置重命名属性和默认值
问题:规范给出的属性名和模型内部的属性名不相同?
答:使用 attribute 配置这种映射,比如: fields.String(attribute='username')
问题:某些字段,没有值,但想给一个值做为默认值?
答:使用 default 指定默认值,比如: fields.String(default='sxt')
示例代码:
from flask import Flask
from flask_restful import Api, Resource, fields, marshal_with
app = Flask(__name__)
api = Api(app)
class News:
def __init__(self, code, msg, info):
self.code = code
self.msg = msg
self.info = info
self.state = 1000
class NewsView(Resource):
resouce_fields = {
'code': fields.Integer(default=200), # 通过参数default来设置默认值
'msg': fields.String,
'content': fields.String(attribute='info'), # 通过参数attribute来设置提取数据的字段
'state': fields.Integer(default=2000) # 优先级不如真实数据里面的高
}
@marshal_with(resouce_fields)
def get(self):
news = News(200, '访问成功!', '移动')
return news
@marshal_with(resouce_fields)
def post(self):
return {'msg': '增加数据成功!', 'info': '联通'}
@marshal_with(resouce_fields)
def put(self):
news = News(200, '访问成功!', '移动')
return news
api.add_resource(NewsView, '/news/')
if __name__ == '__main__':
app.run(debug=True)
运行结果:
4、Restful规范返回值-类型设置
大型的互联网项目中,返回的数据格式,有时是比较复杂的结构。
如:豆瓣电影
返回的值里有json或者列表数据,这时可以通过以字段来实现
- fields.List 放置一个列表
- fields.Nested放置一个字典
示例代码:
from flask import Flask
from flask_restful import Api, Resource, fields, marshal_with
app = Flask(__name__)
api = Api(app)
class User:
def __init__(self, uname):
self.uname = uname
def __repr__(self):
return f'<User uname:{self.uname}>'
class NewsType:
def __init__(self, _type):
self._type = _type
def __repr__(self):
return f'<User type:{self._type}>'
class News:
def __init__(self, code, msg):
self.code = code
self.msg = msg
self.user = None
self._type = []
def __repr__(self):
return f'<News code:{self.code} msg:{self.msg} user:{self.user} type:{self._type}>'
def create_data():
user = User('dgw')
_type1 = NewsType('IT')
_type2 = NewsType('Python')
news = News(200, 'Python update!')
news.user = user
news._type.append(_type1)
news._type.append(_type2)
return news
class NewsView(Resource):
resouce_fields = {
'code': fields.Integer,
'msg': fields.String,
'user': fields.Nested({
'uname': fields.String
}),
'_type': fields.List(fields.Nested({
'_type': fields.String
}))
}
@marshal_with(resouce_fields)
def get(self):
news = create_data()
print(news)
print(type(news))
return news
api.add_resource(NewsView, '/news/')
if __name__ == '__main__':
app.run(debug=True)
运行结果:
5、定制返回的JSON格式
5.1 需求
想要接口返回的JSON数据具有如下统一的格式
{"message": "描述信息", "data": {要返回的具体数据}}
在接口处理正常的情况下, message返回ok即可,但是若想每个接口正确返回时省略message字段
class DemoResource(Resource):
def get(self):
return {'user_id':1, 'name': 'itcast'}
对于诸如此类的接口,能否在某处统一格式化成上述需求格式?
{"message": "OK", "data": {'user_id':1, 'name': 'itcast'}}
5.2 解决
Flask-RESTful的Api对象提供了一个representation
的装饰器,允许定制返回数据的呈现格式
api = Api(app)
@api.representation('application/json')
def handle_json(data, code, headers):
# TODO 此处添加自定义处理
return resp
Flask-RESTful原始对于json的格式处理方式如下:
代码出处:flask_restful.representations.json
from flask import make_response, current_app
from flask_restful.utils import PY3
from json import dumps
def output_json(data, code, headers=None):
"""Makes a Flask response with a JSON encoded body"""
settings = current_app.config.get('RESTFUL_JSON', {})
# If we're in debug mode, and the indent is not set, we set it to a
# reasonable value here. Note that this won't override any existing value
# that was set. We also set the "sort_keys" value.
if current_app.debug:
settings.setdefault('indent', 4)
settings.setdefault('sort_keys', not PY3)
# always end the json dumps with a new line
# see https://github.com/mitsuhiko/flask/pull/1262
dumped = dumps(data, **settings) + "\n"
resp = make_response(dumped, code)
resp.headers.extend(headers or {})
return resp
为满足需求,做如下改动即可
@api.representation('application/json')
def output_json(data, code, headers=None):
"""Makes a Flask response with a JSON encoded body"""
# 此处为自己添加***************
if 'message' not in data:
data = {
'message': 'OK',
'data': data
}
# **************************
settings = current_app.config.get('RESTFUL_JSON', {})
# If we're in debug mode, and the indent is not set, we set it to a
# reasonable value here. Note that this won't override any existing value
# that was set. We also set the "sort_keys" value.
if current_app.debug:
settings.setdefault('indent', 4)
settings.setdefault('sort_keys', not PY3)
# always end the json dumps with a new line
# see https://github.com/mitsuhiko/flask/pull/1262
dumped = dumps(data, **settings) + "\n"
resp = make_response(dumped, code)
resp.headers.extend(headers or {})
return resp
示例代码:
from flask import Flask
from flask_restful import Api, Resource, fields, marshal_with
from flask import make_response, current_app
from flask_restful.utils import PY3
from json import dumps
app = Flask(__name__)
api = Api(app)
@api.representation('application/json')
def output_json(data, code, headers=None):
"""Makes a Flask response with a JSON encoded body"""
# 此处为自己添加***************
if 'message' not in data:
data = {
'message': 'OK',
'data': data
}
# **************************
settings = current_app.config.get('RESTFUL_JSON', {})
# If we're in debug mode, and the indent is not set, we set it to a
# reasonable value here. Note that this won't override any existing value
# that was set. We also set the "sort_keys" value.
if current_app.debug:
settings.setdefault('indent', 4)
settings.setdefault('sort_keys', not PY3)
# always end the json dumps with a new line
# see https://github.com/mitsuhiko/flask/pull/1262
dumped = dumps(data, **settings) + "\n"
resp = make_response(dumped, code)
resp.headers.extend(headers or {})
return resp
class NewsView(Resource):
resouce_fields = {
'code': fields.Integer,
'msg': fields.String,
'state': fields.String
}
@marshal_with(resouce_fields)
def get(self):
return {'code': 200.1, 'msg': '访问成功!', 'state': 'OK'}
api.add_resource(NewsView, '/news')
if __name__ == '__main__':
app.run(debug=True)
运行结果: