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
一个小例子:

  1. from flask import Flask
  2. from flask_restful import Api, Resource
  3. app = Flask(__name__)
  4. # 通过Api来绑定app
  5. api = Api(app)
  6. class IndexView(Resource):
  7. def get(self):
  8. return {"username": "gongzhujun"}
  9. def post(self):
  10. return {"password": "147456"}
  11. api.add_resource(IndexView, "/", endpoint="home")
  12. if __name__ == '__main__':
  13. app.run(debug=True)

返回结果:get请求
image.png
返回结果:post请求-postman模仿
image.png

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”: “龚祝俊”}

  1. def post(self):
  2. # 这里的reqparse.RequestParser类似于表单验证
  3. parse = reqparse.RequestParser()
  4. # type 类型
  5. # help 错误后报错的类型
  6. # required=True表示的是该字段必须填写
  7. # choices = [xxx, yyy]可以在其中进行选择
  8. parse.add_argument("username", type=str, help="用户名填写错误", required=True)
  9. parse.add_argument("gender", type=str, help="性别错误", choices=["男", "女"])
  10. args = parse.parse_args()
  11. print(args)
  12. return {"info": "登录成功"}

这里的add_resource和之前的add_url_rule

api.add_resource(IndexView, “/“, endpoint=”index”)

if name == ‘main‘: app.run(debug=True)

  1. **post请求返回值:**<br />**填写错误的情况:在页面中返回字段:help中的报错信息**<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1227988/1589185481010-3c372f48-7b32-4f0e-bd24-8708a520ed2f.png#align=left&display=inline&height=443&margin=%5Bobject%20Object%5D&name=image.png&originHeight=443&originWidth=742&size=31404&status=done&style=none&width=742)<br />**本地页面:不打印任何信息**<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1227988/1589185565305-f0986970-08f7-41c2-ad3a-89947c120560.png#align=left&display=inline&height=76&margin=%5Bobject%20Object%5D&name=image.png&originHeight=76&originWidth=894&size=17734&status=done&style=none&width=894)<br />**在填写正确的情况下:返回正确的return中的内容**<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1227988/1589185657459-d65fe8b4-8e3e-4607-8011-ea72df26d6cd.png#align=left&display=inline&height=431&margin=%5Bobject%20Object%5D&name=image.png&originHeight=431&originWidth=752&size=30290&status=done&style=none&width=752)<br />**本地返回:在写了args = parse.parse_args()的情况下**<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1227988/1589185773200-ce05faa1-f706-4133-9732-b756e7004f95.png#align=left&display=inline&height=64&margin=%5Bobject%20Object%5D&name=image.png&originHeight=64&originWidth=997&size=18531&status=done&style=none&width=997)<br />**add_argument可以指定这个字段的名字,这个字段的数据类型等**
  2. - **default:默认值,如果这个参数没有值,那么将使用这个参数指定的值。**
  3. - required:是否必须。默认为False,如果设置为True,那么这个参数就必须提交上来。
  4. - type:这个参数的数据类型,如果指定,那么将使用指定的数据类型来强制转换提交上来的值。
  5. - choices:选项。提交上来的值只有满足这个选项中的值才符合验证通过,否则验证不通过。
  6. - help:错误信息。如果验证失败后,将会使用这个参数指定的值作为错误信息。
  7. - **trim:是否要去掉前后的空格。**
  8. - **额外的inputs类(url regex等)**
  9. **事例:对输入号码以及网址进行验证**
  10. ```python
  11. from flask import Flask
  12. # 导入inputs,让type更加丰富
  13. from flask_restful import Api, Resource, reqparse, inputs
  14. app = Flask(__name__)
  15. api = Api(app)
  16. class IndexView(Resource):
  17. def get(self):
  18. return {"username": "龚祝俊"}
  19. def post(self):
  20. # 这里的reqparse.RequestParser类似于表单验证
  21. parse = reqparse.RequestParser()
  22. # type 类型
  23. # help 错误后报错的类型
  24. # required=True表示的是该字段必须填写
  25. # choices = [xxx, yyy]可以在其中进行选择
  26. parse.add_argument("username", type=str, help="用户名填写错误", required=True)
  27. parse.add_argument("gender", type=str, help="性别错误", choices=["男", "女"])
  28. parse.add_argument("url", type=inputs.url, help="网址输入错误")
  29. parse.add_argument("telephone", type=inputs.regex(r"1[3]/d{9}"), help="电话号码错误", required=True)
  30. args = parse.parse_args()
  31. print(args)
  32. return {"info": "登录成功"}
  33. api.add_resource(IndexView, "/", endpoint="index")
  34. if __name__ == '__main__':
  35. app.run(debug=True)

post请求:
image.png
**

3、输出字段(更加注重的是输出)

对于一个视图函数,你可以指定好一些字段用于返回。以后可以使用ORM模型或者自定义的模型的时候,它会自动的获取模型中的相应的字段,生成json数据,然后再返回给客户端。这其中需要导入marshal_with装饰器并且需要写一个字典,来指示需要返回的字段,以及该字段的数据类型

  1. from flask import Flask
  2. # 导入fields类(定义对应字段的类型)以及marshal_with装饰器
  3. from flask_restful import Api, Resource, fields, marshal_with
  4. app = Flask(__name__)
  5. api = Api(app)
  6. class LookView(Resource):
  7. # 通过fields定义相关的字段
  8. resource_fields = {
  9. "class": fields.String,
  10. "teacher_name": fields.String,
  11. "student_name": fields.String
  12. }
  13. # 通过marshal_with装饰器(不要忘了将上面定义的字典进行传参)
  14. @marshal_with(resource_fields)
  15. def get(self):
  16. return {"class": "物流三班"}
  17. api.add_resource(LookView, "/", endpoint="lookview")
  18. if __name__ == '__main__':
  19. app.run(port=8000)

代码解读:
导入fields类和marshal_with装饰器

  1. from flask_restful import fields, marshal_with

定义需要输出的字段(通过fields来定义字段的类型)

  1. # 通过定义字典的方式,来确定相关的字段
  2. fields_all = {
  3. "class": fields.String,
  4. "teacher_name": fields.String,
  5. "student_name": fields.String
  6. }

使用marshal_with装饰器(get方法在装饰器之下)

  1. @marshal_with(fields_all)
  2. def get(self):
  3. return {
  4. "class": "物流一班",
  5. "teacher_name": "龚祝俊"
  6. }

注意:在使用该装饰器后,如果在定义的字段中存在该值,则正常返回,如果没有则返回null
image.png

4、重命名属性

很多时候你面向公众的字段名称是不同于内部的属性名。使用 attribute可以配置这种映射。比如现在想要返回user.school中的值,但是在返回给外面的时候,想以education返回回去,那么可以这样写

  1. resource_fields = {
  2. # Teacher_name需要显示出去的名称, attribute="name"原来的名称
  3. 'Teacher_name': fields.Integer(attribute="name")
  4. }

5、默认值

在返回一些字段的时候,有时候可能没有值,那么这时候可以在指定fields的时候给定一个默认值

  1. resource_fields = {
  2. 'age': fields.Integer(default=18)
  3. }

6、复杂结构

有时候想要在返回的数据格式中,形成比较复杂的结构。那么可以使用一些特殊的字段来实现。比如要在一个字段中放置一个列表,那么可以使用fields.List,比如在一个字段下面又是一个字典,那么可以使用fields.Nested。

  1. class ProfileView(Resource):
  2. resource_fields = {
  3. 'username': fields.String,
  4. 'age': fields.Integer,
  5. 'school': fields.String,
  6. 'tags': fields.List(fields.String),
  7. 'more': fields.Nested({
  8. 'signature': fields.String
  9. })
  10. }

4、非常重要的例子(上面内容的总结)

通过manage.py定义数据库,相关内容如下:

  1. from exts import db
  2. # 构建教师表与教视表之间的多对多关系(这里不需要使用元数据)
  3. Class_Teachers = db.Table(
  4. "class_teachers",
  5. db.Column("class_id", db.Integer, db.ForeignKey("class.id", ondelete="SET NULL")),
  6. db.Column("teachers_id", db.Integer, db.ForeignKey("teachers.id", ondelete="SET NULL"))
  7. )
  8. class Class(db.Model):
  9. __tablename__ = "class"
  10. id = db.Column(db.Integer, primary_key=True, autoincrement=True)
  11. classroom = db.Column(db.String(50), nullable=False)
  12. floor = db.Column(db.Integer, default=3)
  13. Teachers = db.relationship("Teachers", secondary=Class_Teachers, backref="Class")
  14. Students = db.relationship("Students", backref="Class")
  15. __mapper_args__ = {
  16. "order_by": floor.desc()
  17. }
  18. class Teachers(db.Model):
  19. __tablename__ = "teachers"
  20. id = db.Column(db.Integer, primary_key=True, autoincrement=True)
  21. name = db.Column(db.String(50), nullable=False)
  22. gender = db.Column(db.Enum("男", "女", "保密"), default="保密")
  23. class Students(db.Model):
  24. __tablename__ = "students"
  25. id = db.Column(db.Integer, primary_key=True, autoincrement=True)
  26. name = db.Column(db.String(50))
  27. age = db.Column(db.Integer)
  28. uid = db.Column(db.Integer, db.ForeignKey("class.id", ondelete="SET NULL"))

通过flask_restful输出数据库中的字段

  1. import config
  2. from exts import db
  3. from flask import Flask
  4. from modules import Class
  5. from flask_restful import Api, Resource, marshal_with, fields, reqparse
  6. app = Flask(__name__)
  7. app.config.from_object(config)
  8. db.init_app(app)
  9. api = Api(app)
  10. class LoginView(Resource):
  11. def get(self):
  12. return {"name": "gongzhujun"}
  13. def post(self):
  14. parse = reqparse.RequestParser()
  15. parse.add_argument("URL", type=inputs.url, help="网址输入错误", required=True)
  16. parse.add_argument("username", type=str, help="用户名输入错误", required=True)
  17. parse.add_argument("password", type=str, help="密码输入错误", required=True)
  18. parse.add_argument("gender", type=str, help="性别有误", choices=["男", "女", "保密"], default="保密")
  19. args = parse.parse_args()
  20. return args
  21. class ListView(Resource):
  22. # 通过定义字典的方式,来确定相关的字段
  23. fields_all = {
  24. "classroom": fields.String,
  25. "floor": fields.Integer,
  26. "Teachers": fields.String,
  27. "Students": fields.String
  28. }
  29. @marshal_with(fields_all)
  30. # 这里的class_id是传入的查询数
  31. def get(self, class_id):
  32. Class_information = Class.query.get(class_id)
  33. return Class_information
  34. api.add_resource(LoginView, "/", endpoint="login")
  35. # 在这里定义的class_id,看了就知道了
  36. api.add_resource(ListView, "/list/<int:class_id>/", endpoint="list")
  37. if __name__ == '__main__':
  38. app.run(port=8000)

就是数据库查询与输出api的结合
返回结果:
image.png
这显然不是我们想要的,所以对代码进行修改

  1. class ListView(Resource):
  2. # 通过定义字典的方式,来确定相关的字段
  3. fields_all = {
  4. "classroom": fields.String,
  5. "floor": fields.Integer,
  6. "Teachers": fields.Nested({
  7. "teacher_name": fields.String(attribute="name"),
  8. "gender": fields.String(default="保密")
  9. }),
  10. "Students": fields.List(fields.Nested({
  11. "student_name": fields.String(attribute="name"),
  12. "age": fields.Integer
  13. }))
  14. }

代码解读:fields.Nested()
对于那些通过外键读取过来的数据,如果不进行处理的话,效果会非常的不直观,因此我们通过fields.Nested来对其进行解析

  1. "Teachers": fields.Nested({
  2. "teacher_name": fields.String(attribute="name"),
  3. "gender": fields.String(default="保密")
  4. })
  5. # fields.Nested({
  6. # "所需字段1":fields.字段类型,
  7. # "所需字段1":fields.字段类型,
  8. # ...
  9. # })

代码解读fields.List()—猜测已经进行过封装了(针对返回多条数据),即使只是写了fields.Nested()也会智能的返回列表,但是为了业务逻辑更加的清晰,还是写上吧

  1. "Students": fields.List(fields.Nested({
  2. "student_name": fields.String(attribute="name"),
  3. "age": fields.Integer
  4. }))
  5. # 对于那些返回多条的数据,使用fields.List()进行处理
  6. # fields.List(fields.Nested({
  7. # "所需字段1":fields.字段类型,
  8. # "所需字段1":fields.字段类型,
  9. # ...
  10. # }))

最终返回效果:
image.png