Python全栈(七)Flask框架之13.Flask-Restful的概念和使用

【拍出不一样的学校】航拍中央财经大学

这是我的学校——财经黄埔,你想来吗o( ̄▽ ̄)ブ

一、Restful API规范

Restful API是用于在前端与后台进行通信的一套规范,使用这个规范可以让前后端开发变得更加简单。

1.协议

采用http或者https协议。

2.数据传输格式

数据之间传输的格式应该使用json形式,而不是xml。

3.url链接

url链接中不能有动词,只能有名词;
对于一些名词,如果出现复数,应该在后面加s。

4.HTTP请求方法

方法 含义 举例
GET 从服务器上获取资源 /users/获取所有用户
/user/id/根据id获取一个用户
POST 在服务器上新创建一个资源 /user/新建一个用户
PUT 在服务器上更新资源(客户端提供所有改变后的数据) /user/id/更新某个id的用户的信息(需要提供用户的所有信息)
PATCH 在服务器上更新资源(客户端只提供需要改变的属性) /user/id/更新某个id的用户信息(只需要提供需要改变的信息)
DELETE 从服务器上删除资源 /user/id/删除一个用户

状态码

状态码 描述 含义
200 OK 服务器成功响应客户端的请求
400 INVALID REQUEST 用户发出的请求有错误,服务器没有进行新建或修改数据的操作
401 Unauthorized 用户没有权限访问这个请求
403 Forbidden 因为某些原因禁止访问这个请求
404 NOT FOUND 用户发送的请求的url不存在
406 NOT Acceptable 用户请求不被服务器接收(比如服务器期望客户端发送某个字段,但是没有发送)
500 Internal server error 服务器内部错误,比如出现了bug

二、Flask-Restful插件的基本使用

1.基本概念

Flask-Restful是Flask中专门用来实现Restful API的插件,使用它可以快速集成Restful API功能。
在app的后台以及纯API的后台中,这个插件可以帮助我们节省很多时间;
在普通的网站中,这个插件显得有些鸡肋,因为在普通的网页开发中,是需要去渲染HTML代码的,而Flask-Restful在每个请求中都返回json格式的数据。

2.安装

Flask-Restful的环境要求:

  • Flask版本>=0.8
  • Python版本为2.6、2.7,或3.3及以上。

在虚拟环境中使用pip install flask-restful命令安装。

3.基本使用

使用Flask-Restful定义视图类时,要继承自flask_restful.Resource类,再根据具体的请求的方法来定义相应的方法。
例如,如果期望用户在客户端中使用get方法发送请求,那么就定义一个get方法,类似于MethodView。
简单使用测试如下:

from flask import Flask
from flask_restful import Api, Resource

app = Flask(__name__)
api = Api(app)


class IndexView(Resource):
    def get(self):
        return {'username': 'Corley'}

    def post(self):
        return {'info': 'Login Successfully!!'}


api.add_resource(IndexView, '/', endpoint='index')

if __name__ == '__main__':
    app.run(debug=True)

运行之后,使用Postman模拟请求如下:
flask restful simple use
显然,用get方法和post方法请求得到的结果是不一样的。
说明:

  • endpoint用来指定使用url_for()方法反转url时传入的参数;如果不设置endpoint参数,会使用视图的名字的小写作为endpoint的值。
  • add_resource()方法的第二个参数是视图函数的路由地址,这个地址与视图函数的route一样,可以传递参数;有一点不同的是,这个方法可以传递多个url来指定视图函数。

三、Restful参数解析

Flask-Restful插件提供了类似WTForms来验证提交的数据是否合法的包,即reqparse
RequestParser对象的add_argument()方法可以指定字段的名字、数据类型等,常见的参数有:

  • default
    默认值,如果参数没有值,那么将使用这个参数指定的值。
  • required
    是否必须。
    默认为False;如果设置为True,则必须提交这个参数。
  • type
    参数的数据类型,如果指定,那么将使用指定的数据类型来强制转换提交得到的值。
  • choices
    选项,提交上来的值只有满足选项中的值才符合验证通过,否则验证不通过。
  • help
    错误信息,如果验证失败后,将会使用help参数指定的值作为错误信息。
  • trim
    是否要去掉前后的空格。

练习如下:

from flask import Flask
from flask_restful import Api, Resource, reqparse, inputs

app = Flask(__name__)
api = Api(app)


class IndexView(Resource):
    def get(self):
        return {'username': 'Corley'}

    def post(self):
        parse = reqparse.RequestParser()
        parse.add_argument('username', type=str, help='用户名验证错误', required=True)
        parse.add_argument('password', type=str, help='密码验证错误', trim=True)
        parse.add_argument('age', type=int, help='年龄错误')
        parse.add_argument('gender', type=str, help='性别错误', choices=['male', 'female', 'secret'], default='secret')
        parse.add_argument('blog', type=inputs.url, help='博客地址错误')
        parse.add_argument('phone', type=inputs.regex(r'1[35789]\d{9}'), help='电话号码错误')

        args = parse.parse_args()
        return {'info': 'Register Successfully!!', 'args': args}


api.add_resource(IndexView, '/', endpoint='index')

if __name__ == '__main__':
    app.run(debug=True)

显示:
flask restful form validate

四、Restful高级使用

1.输出字段

对于一个视图类,可以预设好一些字段用于返回,以后使用ORM模型或者自定义模型的时候,会自动获取模型中相应的字段,生成json数据,再返回。
这需要导入flask_restful.marshal_with装饰器,在视图类中定义一个字典来指定需要返回的字段,以及该字段的数据类型。

测试如下:

from flask import Flask
from flask_restful import Api, Resource, reqparse, inputs, fields, marshal_with

app = Flask(__name__)
api = Api(app)


class IndexView(Resource):
    def get(self):
        return {'username': 'Corley'}

    def post(self):
        parse = reqparse.RequestParser()
        parse.add_argument('username', type=str, help='用户名验证错误', required=True)
        parse.add_argument('password', type=str, help='密码验证错误', trim=True)
        parse.add_argument('age', type=int, help='年龄错误')
        parse.add_argument('gender', type=str, help='性别错误', choices=['male', 'female', 'secret'], default='secret')
        parse.add_argument('blog', type=inputs.url, help='博客地址错误')
        parse.add_argument('phone', type=inputs.regex(r'1[35789]\d{9}'), help='电话号码错误')

        args = parse.parse_args()
        return {'info': 'Register Successfully!!', 'args': args}


class ArticleView(Resource):
    resource_fields = {
        'title': fields.String,
        'content': fields.String
    }

    @marshal_with(resource_fields)
    def get(self):
        return {'title': 'Python Flask'}


api.add_resource(IndexView, '/', endpoint='index')
api.add_resource(ArticleView, '/article/', endpoint='article')

if __name__ == '__main__':
    app.run(debug=True)

显示:
flask restful output field

显然,get方法使用了marshal_with装饰器后,即使返回时未定义content字段,在网页中也会渲染该字段。

还可以在get方法中直接返回数据库的查询结果,示例如下:

class ArticleView(Resource):
    resource_fields = {
        'title': fields.String,
        'content': fields.String,
        'tags': fields.String
    }

    @marshal_with(resource_fields)
    def get(self, article_id):
    	article = Article.query.get(article_id)
        return article

在get方法中返回article的时候,flask_restful会自动读取Article模型上的title、content和tags属性,组装成json格式的字符串返回给客户端。

2.复杂结构与格式化输出

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

练习如下:
在当前项目目录下创建主程序文件flask_restful_test.py如下:

from flask import Flask
from flask_restful import Api, Resource, fields, marshal_with
import config
from exts import db
from models import Article

app = Flask(__name__)
api = Api(app)
app.config.from_object(config)
db.init_app(app)


@app.route('/')
def index():
    return '这是首页'


class ArticleView(Resource):
    resource_fields = {
        'title': fields.String,
        'content': fields.String,
        'author': fields.Nested({
            'username': fields.String,
            'email': fields.String
        }),
        'tags': fields.List({
            'id': fields.Integer,
            'name': fields.String
        })
    }

    @marshal_with(resource_fields)
    def get(self, article_id):
        article = Article.query.get(article_id=article_id)
        return article


api.add_resource(ArticleView, '/article/<article_id>', endpoint='article')

if __name__ == '__main__':
    app.run(debug=True)

创建配置文件config.py如下:

# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_restful'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)

SQLALCHEMY_DATABASE_URI = DB_URL
SQLALCHEMY_TRACK_MODIFICATIONS = False

创建模型文件models.py如下:

from exts import db


class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(30))
    email = db.Column(db.String(50))


article_tag_table = db.Table(
    'article_tag',
    db.Column('article_id', db.Integer, db.ForeignKey('article.id')),
    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'))
)


class Article(db.Model):
    __tablename__ = 'article'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(30))
    content = db.Column(db.String(500))
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    author = db.relationship('User', backref='articles')
    tags = db.relationship('Tag', secondary=article_tag_table, backref='tags')


class Tag(db.Model):
    __tablename__ = 'tag'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(30))

创建中间文件exts.py如下:

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

创建数据库迁移映射文件manage.py如下:

from flask_script import Manager
from flask_restful_test import app
from flask_migrate import Migrate, MigrateCommand
from exts import db
import models

manager = Manager(app)
Migrate(app, db)

manager.add_command('db', MigrateCommand)

if __name__ == '__main__':
    manager.run()

在命令行当前目录下依次执行python manage.py db initpython manage.py db migratepython manage.py db upgrade,执行成功后可以在flask_restful数据库中看到建立了四个表,为了方便,手动插入数据如下:

mysql> select * from user;                          
+----+----------+-------------+                     
| id | username | email       |                     
+----+----------+-------------+                     
|  1 | Corley   | 123@163.com |                     
+----+----------+-------------+                     
1 row in set (0.00 sec)                             
                                                    
mysql> select * from article;                       
+----+--------------+------------------+-----------+
| id | title        | content          | author_id |
+----+--------------+------------------+-----------+
|  1 | Python Flask | Migrate, Restful |         1 |
+----+--------------+------------------+-----------+
1 row in set (0.00 sec)                             
                                                    
mysql> select * from tag;                           
+----+-------------+                                
| id | name        |                                
+----+-------------+                                
|  1 | Python      |                                
|  2 | Programming |                                
+----+-------------+                                
2 rows in set (0.00 sec)                            
                                                    
mysql> select * from article_tag;                   
+------------+--------+                             
| article_id | tag_id |                             
+------------+--------+                             
|          1 |      1 |                             
|          1 |      2 |                             
+------------+--------+                             
2 rows in set (0.01 sec)                            
                                                    

运行主程序后,访问http://127.0.0.1:5000/article/1,可以看到:
flask restful complex json
显然, 得到了美化的json格式的数据。

3.重命名属性

很多时候面向用户的字段名称是不同于内部的属性名,此时使用attribute参数可以配置这种映射。
比如想要在返回article的id时表现的形式是article_id,可以写成'article_title': fields.String(attribute='title')

主程序文件中属性重命名示例如下:

from flask import Flask
from flask_restful import Api, Resource, fields, marshal_with
import config
from exts import db
from models import Article

app = Flask(__name__)
api = Api(app)
app.config.from_object(config)
db.init_app(app)


@app.route('/')
def index():
    return '这是首页'


class ArticleView(Resource):
    resource_fields = {
        'article_title': fields.String(attribute='title'),
        'article_content': fields.String(attribute='content'),
        'article_author': fields.Nested({
            'username': fields.String,
            'email': fields.String
        }, attribute='author'),
        'article_tags': fields.List(fields.Nested({
            'id': fields.Integer,
            'name': fields.String
        }), attribute='tags')
    }

    @marshal_with(resource_fields)
    def get(self, article_id):
        article = Article.query.get(article_id)
        return article


api.add_resource(ArticleView, '/article/<article_id>', endpoint='article')

if __name__ == '__main__':
    app.run(debug=True)

再访问http://127.0.0.1:5000/article/1,看到:
flask restful complex json rename
显然, 显示的属性是重命名之后的属性。

4.指定默认值

在返回一些字段的时候,有时候可能没有值,此时可以在指定fields时使用default参数给定一个默认值

修改主程序文件实现指定默认值示例如下:

from flask import Flask
from flask_restful import Api, Resource, fields, marshal_with
import config
from exts import db
from models import Article

app = Flask(__name__)
api = Api(app)
app.config.from_object(config)
db.init_app(app)


@app.route('/')
def index():
    return '这是首页'


class ArticleView(Resource):
    resource_fields = {
        'article_title': fields.String(attribute='title'),
        'article_content': fields.String(attribute='content'),
        'article_author': fields.Nested({
            'username': fields.String,
            'email': fields.String
        }, attribute='author'),
        'article_tags': fields.List(fields.Nested({
            'id': fields.Integer,
            'name': fields.String
        }), attribute='tags'),
        'read_count': fields.Integer(default=0)
    }

    @marshal_with(resource_fields)
    def get(self, article_id):
        article = Article.query.get(article_id)
        return article


api.add_resource(ArticleView, '/article/<article_id>', endpoint='article')

if __name__ == '__main__':
    app.run(debug=True)

再访问http://127.0.0.1:5000/article/1,看到:
flask restful complex json default
显然, 增加了文章的默认阅读量为0,返回的页面中也将其渲染出来。
在给模型中存在的字段设置默认值时,如果数据库中对应记录的该字段的值存在,则页面渲染数据库中的值;
如果数据库中该字段的值不存在即为空时,则渲染给定的默认值。

猜你喜欢

转载自blog.csdn.net/CUFEECR/article/details/106197809