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
    錯誤信息,如果驗證失敗後,將會使用這個參數指定的值作爲錯誤信息。
  • 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時給定一個默認值

修改主程序文件實現指定默認值示例如下:

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,返回的頁面中也將其渲染出來。
在給模型中存在的字段設置默認值時,如果數據庫中對應記錄的該字段的值存在,則頁面渲染數據庫中的值;
如果數據庫中該字段的值不存在即爲空時,則渲染給定的默認值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章