1、简介
Flask-Restful是一个专门用来写restful api的一个插件。使用它可以快速的集成restful api功能。在app的后台以及纯api的后台中,这个插件可以帮助我们节省很多时间。当然,如果在普通的网站中,这个插件就显得有些鸡肋了,因为在普通的网页开发中,是需要去渲染HTML代码的,而Flask-Restful在每个请求中都是返回json格式的数据
2、安装
Flask-Restful需要在Flask 0.8以上的版本,在Python2.6或者Python3.3上运行。通过pip install flask-restful即可安装
3、定义Restful的视图
如果使用Flask-Restful,那么定义视图函数的时候,就要继承自flask_restful.Resource类,然后再根据当前请求的method来定义相应的方法。比如期望客户端是使用get方法发送过来的请求,那么就定义一个get方法。类似于MethodView
一个小例子:
from flask import Flask
from flask_restful import Api, Resource
app = Flask(__name__)
# 通过Api来绑定app
api = Api(app)
class IndexView(Resource):
def get(self):
return {"username": "gongzhujun"}
def post(self):
return {"password": "147456"}
api.add_resource(IndexView, "/", endpoint="home")
if __name__ == '__main__':
app.run(debug=True)
返回结果:get请求
返回结果:post请求-postman模仿
1、注意事项
- endpoint是用来给url_for反转url的时候指定的。如果不写endpoint,那么将会使用视图的名字的小写来作为endpoint。
- add_resource的第二个参数是访问这个视图函数的url,这个url可以跟之前的route一样,可以传递参数。并且还有一点不同的是,这个方法可以传递多个url来指定这个视图函数
2、参数解析(更加注重的是对上传参数的一个验证)
Flask-Restful插件提供了类似WTForms来验证提交的数据是否合法的包,叫做reqparse ```python from flask import Flask from flask_restful import Api, Resource, reqparse
app = Flask(name) api = Api(app)
class IndexView(Resource): def get(self): return {“username”: “龚祝俊”}
def post(self):
# 这里的reqparse.RequestParser类似于表单验证
parse = reqparse.RequestParser()
# type 类型
# help 错误后报错的类型
# required=True表示的是该字段必须填写
# choices = [xxx, yyy]可以在其中进行选择
parse.add_argument("username", type=str, help="用户名填写错误", required=True)
parse.add_argument("gender", type=str, help="性别错误", choices=["男", "女"])
args = parse.parse_args()
print(args)
return {"info": "登录成功"}
这里的add_resource和之前的add_url_rule
api.add_resource(IndexView, “/“, endpoint=”index”)
if name == ‘main‘: app.run(debug=True)
**post请求返回值:**<br />**填写错误的情况:在页面中返回字段:help中的报错信息**<br /><br />**本地页面:不打印任何信息**<br /><br />**在填写正确的情况下:返回正确的return中的内容**<br /><br />**本地返回:在写了args = parse.parse_args()的情况下**<br /><br />**add_argument可以指定这个字段的名字,这个字段的数据类型等**
- **default:默认值,如果这个参数没有值,那么将使用这个参数指定的值。**
- required:是否必须。默认为False,如果设置为True,那么这个参数就必须提交上来。
- type:这个参数的数据类型,如果指定,那么将使用指定的数据类型来强制转换提交上来的值。
- choices:选项。提交上来的值只有满足这个选项中的值才符合验证通过,否则验证不通过。
- help:错误信息。如果验证失败后,将会使用这个参数指定的值作为错误信息。
- **trim:是否要去掉前后的空格。**
- **额外的inputs类(url regex等)**
**事例:对输入号码以及网址进行验证**
```python
from flask import Flask
# 导入inputs,让type更加丰富
from flask_restful import Api, Resource, reqparse, inputs
app = Flask(__name__)
api = Api(app)
class IndexView(Resource):
def get(self):
return {"username": "龚祝俊"}
def post(self):
# 这里的reqparse.RequestParser类似于表单验证
parse = reqparse.RequestParser()
# type 类型
# help 错误后报错的类型
# required=True表示的是该字段必须填写
# choices = [xxx, yyy]可以在其中进行选择
parse.add_argument("username", type=str, help="用户名填写错误", required=True)
parse.add_argument("gender", type=str, help="性别错误", choices=["男", "女"])
parse.add_argument("url", type=inputs.url, help="网址输入错误")
parse.add_argument("telephone", type=inputs.regex(r"1[3]/d{9}"), help="电话号码错误", required=True)
args = parse.parse_args()
print(args)
return {"info": "登录成功"}
api.add_resource(IndexView, "/", endpoint="index")
if __name__ == '__main__':
app.run(debug=True)
3、输出字段(更加注重的是输出)
对于一个视图函数,你可以指定好一些字段用于返回。以后可以使用ORM模型或者自定义的模型的时候,它会自动的获取模型中的相应的字段,生成json数据,然后再返回给客户端。这其中需要导入marshal_with装饰器。并且需要写一个字典,来指示需要返回的字段,以及该字段的数据类型
from flask import Flask
# 导入fields类(定义对应字段的类型)以及marshal_with装饰器
from flask_restful import Api, Resource, fields, marshal_with
app = Flask(__name__)
api = Api(app)
class LookView(Resource):
# 通过fields定义相关的字段
resource_fields = {
"class": fields.String,
"teacher_name": fields.String,
"student_name": fields.String
}
# 通过marshal_with装饰器(不要忘了将上面定义的字典进行传参)
@marshal_with(resource_fields)
def get(self):
return {"class": "物流三班"}
api.add_resource(LookView, "/", endpoint="lookview")
if __name__ == '__main__':
app.run(port=8000)
代码解读:
导入fields类和marshal_with装饰器
from flask_restful import fields, marshal_with
定义需要输出的字段(通过fields来定义字段的类型)
# 通过定义字典的方式,来确定相关的字段
fields_all = {
"class": fields.String,
"teacher_name": fields.String,
"student_name": fields.String
}
使用marshal_with装饰器(get方法在装饰器之下)
@marshal_with(fields_all)
def get(self):
return {
"class": "物流一班",
"teacher_name": "龚祝俊"
}
注意:在使用该装饰器后,如果在定义的字段中存在该值,则正常返回,如果没有则返回null
4、重命名属性
很多时候你面向公众的字段名称是不同于内部的属性名。使用 attribute可以配置这种映射。比如现在想要返回user.school中的值,但是在返回给外面的时候,想以education返回回去,那么可以这样写
resource_fields = {
# Teacher_name需要显示出去的名称, attribute="name"原来的名称
'Teacher_name': fields.Integer(attribute="name")
}
5、默认值
在返回一些字段的时候,有时候可能没有值,那么这时候可以在指定fields的时候给定一个默认值
resource_fields = {
'age': fields.Integer(default=18)
}
6、复杂结构
有时候想要在返回的数据格式中,形成比较复杂的结构。那么可以使用一些特殊的字段来实现。比如要在一个字段中放置一个列表,那么可以使用fields.List,比如在一个字段下面又是一个字典,那么可以使用fields.Nested。
class ProfileView(Resource):
resource_fields = {
'username': fields.String,
'age': fields.Integer,
'school': fields.String,
'tags': fields.List(fields.String),
'more': fields.Nested({
'signature': fields.String
})
}
4、非常重要的例子(上面内容的总结)
通过manage.py定义数据库,相关内容如下:
from exts import db
# 构建教师表与教视表之间的多对多关系(这里不需要使用元数据)
Class_Teachers = db.Table(
"class_teachers",
db.Column("class_id", db.Integer, db.ForeignKey("class.id", ondelete="SET NULL")),
db.Column("teachers_id", db.Integer, db.ForeignKey("teachers.id", ondelete="SET NULL"))
)
class Class(db.Model):
__tablename__ = "class"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
classroom = db.Column(db.String(50), nullable=False)
floor = db.Column(db.Integer, default=3)
Teachers = db.relationship("Teachers", secondary=Class_Teachers, backref="Class")
Students = db.relationship("Students", backref="Class")
__mapper_args__ = {
"order_by": floor.desc()
}
class Teachers(db.Model):
__tablename__ = "teachers"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(50), nullable=False)
gender = db.Column(db.Enum("男", "女", "保密"), default="保密")
class Students(db.Model):
__tablename__ = "students"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(50))
age = db.Column(db.Integer)
uid = db.Column(db.Integer, db.ForeignKey("class.id", ondelete="SET NULL"))
通过flask_restful输出数据库中的字段
import config
from exts import db
from flask import Flask
from modules import Class
from flask_restful import Api, Resource, marshal_with, fields, reqparse
app = Flask(__name__)
app.config.from_object(config)
db.init_app(app)
api = Api(app)
class LoginView(Resource):
def get(self):
return {"name": "gongzhujun"}
def post(self):
parse = reqparse.RequestParser()
parse.add_argument("URL", type=inputs.url, help="网址输入错误", required=True)
parse.add_argument("username", type=str, help="用户名输入错误", required=True)
parse.add_argument("password", type=str, help="密码输入错误", required=True)
parse.add_argument("gender", type=str, help="性别有误", choices=["男", "女", "保密"], default="保密")
args = parse.parse_args()
return args
class ListView(Resource):
# 通过定义字典的方式,来确定相关的字段
fields_all = {
"classroom": fields.String,
"floor": fields.Integer,
"Teachers": fields.String,
"Students": fields.String
}
@marshal_with(fields_all)
# 这里的class_id是传入的查询数
def get(self, class_id):
Class_information = Class.query.get(class_id)
return Class_information
api.add_resource(LoginView, "/", endpoint="login")
# 在这里定义的class_id,看了就知道了
api.add_resource(ListView, "/list/<int:class_id>/", endpoint="list")
if __name__ == '__main__':
app.run(port=8000)
就是数据库查询与输出api的结合
返回结果:
这显然不是我们想要的,所以对代码进行修改
class ListView(Resource):
# 通过定义字典的方式,来确定相关的字段
fields_all = {
"classroom": fields.String,
"floor": fields.Integer,
"Teachers": fields.Nested({
"teacher_name": fields.String(attribute="name"),
"gender": fields.String(default="保密")
}),
"Students": fields.List(fields.Nested({
"student_name": fields.String(attribute="name"),
"age": fields.Integer
}))
}
代码解读:fields.Nested()
对于那些通过外键读取过来的数据,如果不进行处理的话,效果会非常的不直观,因此我们通过fields.Nested来对其进行解析
"Teachers": fields.Nested({
"teacher_name": fields.String(attribute="name"),
"gender": fields.String(default="保密")
})
# fields.Nested({
# "所需字段1":fields.字段类型,
# "所需字段1":fields.字段类型,
# ...
# })
代码解读fields.List()—猜测已经进行过封装了(针对返回多条数据),即使只是写了fields.Nested()也会智能的返回列表,但是为了业务逻辑更加的清晰,还是写上吧
"Students": fields.List(fields.Nested({
"student_name": fields.String(attribute="name"),
"age": fields.Integer
}))
# 对于那些返回多条的数据,使用fields.List()进行处理
# fields.List(fields.Nested({
# "所需字段1":fields.字段类型,
# "所需字段1":fields.字段类型,
# ...
# }))
最终返回效果: