1. (类)视图
1.1 基本使用
1. 扩展介绍
- Flask-RESTful是一个Flask扩展,它添加了快速构建REST APIs的支持。
环境安装
pip install flask-restful
文档
- 官方文档 https://flask-restful.readthedocs.io/en/latest
- 中文翻译 http://www.pythondoc.com/Flask-RESTful/index.html
2. 构建RESTAPI
- 使用
flask-restful
构建RESTAPI只需要三步操作- 创建扩展/组件对象
- 组件对象 = Api(app)
- 定义类视图
- class 自定义视图类(Resource)
- 组件添加类视图
- 组件对象.add_resource(视图类,URL资源段)
- 创建扩展/组件对象
from flask import Flask
from flask_restful import Api, Resource
app = Flask(__name__)
# 1. 创建API对象 用于管理类视图(资源)
api = Api(app)
# 2.定义类视图 继承Resource
class DemoResource(Resource):
def get(self):
# 类视图响应的content-type默认变为json形式
# 类视图的返回值可以是字典, 会被自动转为json字符串
return {'foo': 'get'}
def post(self):
return {'foo': 'post'}
# 3.添加类视图 函数标记默认为类名小写
api.add_resource(DemoResource, '/', endpoint='demo')
# @app.route('/')
# def index():
#
# return "index"
if __name__ == '__main__':
print(app.url_map)
app.run(debug=True)
- 类视图的优点
- 类视图相应的***content-type***默认变为JSON形式
- 类视图的***返回值可以是字典***,会被自动转化为json字符串
1.2 类视图装饰器
- 通过***method_decorators***类属性来设置类视图的装饰器
- 该属性接收两种类型s数据
- 列表形式:所有请求方式都会使用列表中的装饰器
- 字典形式:给请求方式分别设置装饰器
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
def deco1(f):
def wrapper(*args, **kwargs):
print('deco1')
return f(*args, **kwargs)
return wrapper
def deco2(f):
def wrapper(*args, **kwargs):
print('deco2')
return f(*args, **kwargs)
return wrapper
class DemoResource(Resource):
# 通过method_decorators类属性来设置类视图的装饰器
# method_decorators = [deco1, deco2] # 列表形式 所有请求方式都会使用
method_decorators = {'get': [deco1], 'post': [deco2]} # 字典形式 给请求方式分别设置装饰器
# @deco2
# @deco1
def get(self):
return {'foo': "get"}
def post(self):
return {'foo': "post"}
api.add_resource(DemoResource, '/')
if __name__ == '__main__':
app.run(debug=True)
1.3 蓝图和类视图
- 蓝图和类视图可以配合使用,步骤如下:
- 创建蓝图对象 蓝图对象 = Blueprint(‘蓝图名’,name)
- 每个蓝图分别创建组件对象 *** 组件对象 = Api(蓝图对象)***
- 组件对象添加类视图 组件对象.add_resource(视图类,URL资源段)
- 应用注册蓝图 应用对象.register_blueprint(蓝图对象)
在 user包中创建 views文件, 定义类视图
# user/views.py
from flask_restful import Resource
class DemoResource(Resource):
def get(self):
return {'get': 'hello'}
def post(self):
return {'post': 'world'}
- 在 user包 的初始化文件 init.py 中:
- 创建蓝图对象
- 通过蓝图对象创建对应的组件对象
- 组件对象添加类视图
# user/__init__.py
from flask import Blueprint
from flask_restful import Api
from user.views import DemoResource
# 1.创建蓝图对象
user_blu = Blueprint('user', __name__, url_prefix='/user')
# 2.创建蓝图对应的api对象
user_api = Api(user_blu)
# 3.添加类视图
user_api.add_resource(DemoResource, '/')
- 想要让蓝图对象能够完成路由定义, 还需要 *** Flask应用注册蓝图对象***
# main.py
from flask import Flask
from user import user_blu
app = Flask(__name__)
# 4.注册蓝图
app.register_blueprint(user_blu)
if __name__ == '__main__':
print(app.url_map)
app.run(debug=True, port=8000)
2. 请求
2.1 请求解析
DRF框架 的核心功能 序列化器, 包括以下功能:
* 反序列化
* 参数解析
* 数据保存
* 序列化
- flask-restful也实现了类似的功能:
- 参数解析 RequestParser
- 序列化 marshal函数
- flask-restful 没有提供数据保存功能, 数据存储可以使用后续的flask-sqlalchemy扩展 来完成
- RequestParser 负责请求解析工作, 基本步骤如下:
- 创建请求解析器 请求解析器 = RequestParser()
- 添加参数规则 请求解析器.add_argument(参数名, 参数规则…)
- 执行解析 参数对象 = 请求解析器.parse_args()
- 获取参数 参数对象.参数名
from flask import Flask
from flask_restful import Resource, Api
from flask_restful.reqparse import RequestParser
app = Flask(__name__)
api = Api(app)
class DemoResource(Resource):
def get(self):
# 1.创建请求解析器
parser = RequestParser()
# 2.添加参数规则
parser.add_argument('name')
parser.add_argument('age')
# 3.执行解析 默认会从 查询字符串/post键值对/post-json数据 进行参数提取
args = parser.parse_args()
# 4.获取参数
print(args.name)
print(args.age)
return {'foo': "get"}
api.add_resource(DemoResource, '/')
if __name__ == '__main__':
app.run(debug=True)
2.2 参数详解
- default & required * location参数
from flask import Flask
from flask_restful import Resource, Api
from flask_restful.reqparse import RequestParser
app = Flask(__name__)
api = Api(app)
class DemoResource(Resource):
def post(self):
parser = RequestParser()
parser.add_argument('name', required=True, location='json')
parser.add_argument('age', default=10)
args = parser.parse_args()
print(args.name)
print(args.age)
return {'foo': "post"}
api.add_resource(DemoResource, '/')
if __name__ == '__main__':
app.run(debug=True)
- type参数
from flask import Flask
from flask_restful import Resource, Api
from flask_restful.reqparse import RequestParser
from flask_restful.inputs import *
app = Flask(__name__)
api = Api(app)
# 自定义函数进行参数校验和转换
def func1(value): # 必须定义形参来接收传递来的参数
if re.match(r'^user:', value):
return value[5:] # 转换完, 还需要将结果返回
else:
raise ValueError('age参数格式错误') # 校验失败, 会将错误信息以json形式返回
class DemoResource(Resource):
def put(self):
parser = RequestParser()
parser.add_argument('name')
# parser.add_argument('age', type=int) # 转为int类型
# parser.add_argument('age', type=boolean) # 转为bool类型 1/0 true/false
# parser.add_argument('age', type=date) # 日期 转为datetime类型 YYYY-mm-dd
# parser.add_argument('age', type=datetime_from_iso8601) # 时间 转为datetime类型 2012-01-01T23:30:00+02:00
# parser.add_argument('age', type=int_range(5, 10)) # 转为int类型 限定范围[5, 10]
# parser.add_argument('age', type=regex(r'^1[3-9]\d{9}$')) # 要求匹配正则
parser.add_argument('age', type=func1) # 自定义函数
args = parser.parse_args()
print(args.name)
print(args.age)
print(type(args.age))
return {'foo': 'put'}
api.add_resource(DemoResource, '/')
if __name__ == '__main__':
app.run(debug=True)
3. 响应
3.1 序列化
- flask-restful 通过 marshal函数 来完成序列化处理
- 操作步骤如下:
- 定义序列化规则 序列化规则 = {字段名: 序列化类型}
- marshal函数 按照序列化规则 将模型对象转为字典 序列化后的字典 = marshal(模型对象, 序列化规则)
- 常用序列化类型
from flask import Flask
from flask_restful import Resource, Api, marshal, fields
app = Flask(__name__)
api = Api(app)
class User: # 定义模型类
def __init__(self):
self.name = 'zs'
self.age = 20
self.height = 1.8
self.scores = [80, 90]
self.info = {
'gender': True
}
fields = { # 序列化规则
'username': fields.String(attribute='name'), # 指定对应的模型属性
'age': fields.Integer(default=10), # 设置默认值
'height': fields.Float,
'scores': fields.List(fields.Integer), # 元素类型唯一
'info': fields.Nested({'gender': fields.Boolean})
}
class DemoResource(Resource):
def get(self):
user1 = User()
# marshal函数可以按照指定的序列化规则将 模型对象 转为 字典
return marshal(user1, fields, envelope='data')
api.add_resource(DemoResource, '/')
if __name__ == '__main__':
app.run(debug=True)
- 序列化还有一种装饰器形式***marshal_with***,使用装饰器形式则视图可以直接返回模型对象
from flask import Flask
from flask_restful import Resource, Api, marshal_with, fields
app = Flask(__name__)
api = Api(app)
class User: # 定义模型类
def __init__(self):
self.name = 'zs'
self.age = 20
self.height = 1.8
self.scores = [80, 90]
self.info = {
'gender': True
}
fields = { # 序列化规则
'username': fields.String(attribute='name'), # 指定对应的模型属性
'age': fields.Integer(default=10), # 设置默认值
'height': fields.Float,
'scores': fields.List(fields.Integer), # 元素类型唯一
'info': fields.Nested({'gender': fields.Boolean})
}
class DemoResource(Resource):
method_decorators = {'post': [marshal_with(fields)]}
def post(self):
user1 = User()
# 如果设置了marshal_with装饰器, 可以直接返回模型对象
return user1
api.add_resource(DemoResource, '/')
if __name__ == '__main__':
app.run(debug=True)
- 实际开发中,考虑到序列化的复杂性,部分公司还会采用***自定义方法***的方式来完成序列化处理
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class User:
def __init__(self):
self.name = 'zs'
self.age = 20
self.height = 1.8
self.scores = [80, 90]
self.info = {
'gender': True
}
def to_dict(self): # 自定义模型转换方法
return {
'name': self.name,
'age': self.age
}
class DemoResource(Resource):
def put(self):
user1 = User()
return user1.to_dict()
api.add_resource(DemoResource, '/')
if __name__ == '__main__':
app.run(debug=True)
3.2 自定义json格式
- 实际开发中,返回的JSON数据汇中除了包含基础数据,往往还需要设置一些***统一的外层包装***,仪表前端进行更好的解析处理
{
"message": "ok", // 外层包装
"code": 200, // 外层包装
"data": { // 基础数据
"name": "张三",
"age": 20
}
}
- ***flask-restful***提供了***api.representation()***装饰器方法,允许开发者自定义返回的数据格式
- 在实现自定义JSON格式是,可以参考flask-restful默认转换函数(字典转json数据)的源代码
- 默认转换函数的源代码
# flask_restful/representations/json.py
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
from collections import OrderedDict
from json import dumps
from flask import Flask, current_app, make_response, Response
from flask_restful import Resource, Api
from six import PY3
app = Flask(__name__)
api = Api(app)
@api.representation('application/json') # 指定响应形式对应的转换函数
def output_json(data, code, headers=None):
"""自定义json形式"""
# 根据flask内置配置, 进行格式处理(缩进/key是否排序等)
settings = current_app.config.get('RESTFUL_JSON', {})
if current_app.debug:
settings.setdefault('indent', 4)
settings.setdefault('sort_keys', not PY3)
# 添加json外层包装
if 'message' not in data: # 判断是否设置了自定义的错误信息
data = {
'message': 'ok',
'data': data
}
# 字典转json字符串
dumped = dumps(data, **settings) + "\n"
# 构建响应对象
resp = make_response(dumped, code)
resp.headers.extend(headers or {})
return resp
class DemoResource(Resource):
def get(self):
return {'foo': "get"}
def post(self):
return {'message': 'parameter error: name', "data": None}
api.add_resource(DemoResource, '/')
if __name__ == '__main__':
app.run(debug=True)
- 实际开发中,还会展开装饰器来设置自定义响应格式
def output_json(data, code, headers=None):
"""自定义json形式"""
# 根据flask内置配置, 进行格式处理(缩进/key是否排序等)
settings = current_app.config.get('RESTFUL_JSON', {})
if current_app.debug:
settings.setdefault('indent', 4)
settings.setdefault('sort_keys', not PY3)
# 添加json外层包装
if 'message' not in data: # 判断是否设置了自定义的错误信息
data = {
'message': 'ok',
'data': data
}
# 字典转json字符串
dumped = dumps(data, **settings) + "\n"
# 构建响应对象
resp = make_response(dumped, code)
resp.headers.extend(headers or {})
return resp
# 展开装饰器的形式设置自定义响应形式
api.representation('application/json')(output_json)