1、簡介
Django 和 Flask 一直都是 Python 開發 Web 的首選,而 Flask 的微內核更適用于現在的云原生微服務框架。但是 Flask 只是一個微型的 Web 引擎,所以我們需要擴展 Flask 使其發揮出更強悍的功能。
python flask框架詳解:https://blog.csdn.net/shifengboy/article/details/114274271
Flask-RESTful
Flask-RESTful 就是 Flask 擴展中的佼佼者,它增加了對快速構建 RESTful API 的支持,將 Flask 封裝了一層,使其更容易、更快速、更便捷的開發 RESTful API。
GitHub:https://github.com/flask-restful/flask-restful
英文文檔:https://flask-restful.readthedocs.io/en/latest/
中文文檔:http://www.pythondoc.com/Flask-RESTful/
Flask-RESTPlus
我們知道 Flask-RESTful 是 Flask 的擴展,而 Flask-RESTPlus 則是 Flask-RESTful 的擴展,對 Flask-RESTful 完全兼容且對其進行增強了接口文檔的支持。
Flask-RESTPlus 提供了一個連貫的裝飾器和工具集合來描述文檔 API 所需要的參數和對象,并使用 Swagger 將其解析成正確的接口文檔。
GitHub:https://github.com/noirbizarre/flask-restplus
Docs:https://flask-restplus.readthedocs.io/en/latest/
Flask-RESTX
既然已經有了很完美的 Flask-RESTPlus,那為什么還需要 Flask-RESTX 呢?
其實在很長時間中我都一直都在使用 Flask-RESTPlus,但是難受的是作者丟了!沒錯,就是物理意義上的丟了,Flask-RESTPlus 這個項目團隊的成員都找不動他了,團隊為了持續維護這個項目只能另開一個分支,將 Flask-RESTPlus 繼續延續下去,繼續延續后的項目就是 Flask-RESTX。Flask-RESTX
?完全兼容?Flask-RESTPlus
,Flask-RESTPlus
?項目里積攢的問題、BUG
?都由?Flask-RESTX
?完全繼承并且社區團隊在積極維護匯總,GitHub:https://github.com/python-restx/flask-restx
Docs:https://flask-restx.readthedocs.io/en/latest/
FastAPI
FastAPI 是獨立于 Flask 的新式 Web 框架,雖然能看到很多 Flask 和相關擴展的影子,但是它也成為不可忽視的 Web 框架之一,而且 FastAPI 還號稱是最快的Python框架之一。
GitHub:https://github.com/tiangolo/fastapi
Docs:https://fastapi.tiangolo.com
2、快速入門
安裝:pip install flask-restful
簡單示例
一個最小的 Flask-RESTful API 像這樣:
from flask import Flask
from flask_restful import Resource, Apiapp = Flask(__name__)
api = Api(app)class HelloWorld(Resource):def get(self):return {'hello': 'world'}api.add_resource(HelloWorld, '/')if __name__ == '__main__':app.run(debug=True)
把上述代碼保存為 api.py 并且在你的 Python 解釋器中運行它。需要注意地是我們已經啟用了?Flask 調試?模式,這種模式提供了代碼的重載以及更好的錯誤信息。調試模式絕不能在生產環境下使用。
$ python api.py
?* Running on http://127.0.0.1:5000/
現在打開一個新的命令行窗口使用 curl 測試你的 API:
$ curl http://127.0.0.1:5000/
{"hello": "world"}
"資源(視圖)?和?路由" 綁定
視圖中的類 需要繼承?flask_restful 中的 Resource
Flask-RESTful 提供的主要構建塊是資源。資源構建在 Flask 可插入視圖之上,只需在資源上定義方法,就可以輕松訪問多個 HTTP 方法。一個 todo 應用程序的基本 CRUD 資源是這樣的:
from flask import Flask, request
from flask_restful import Resource, Apiapp = Flask(__name__)
api = Api(app)todos = {}class TodoSimple(Resource):def get(self, todo_id):return {todo_id: todos[todo_id]}def put(self, todo_id):todos[todo_id] = request.form['data']return {todo_id: todos[todo_id]}api.add_resource(TodoSimple, '/<string:todo_id>')if __name__ == '__main__':app.run(debug=True)
你可以嘗試這樣:
$ curl http://localhost:5000/todo1 -d "data=Remember the milk" -X PUT
{"todo1": "Remember the milk"}
$ curl http://localhost:5000/todo1
{"todo1": "Remember the milk"}
$ curl http://localhost:5000/todo2 -d "data=Change my brakepads" -X PUT
{"todo2": "Change my brakepads"}
$ curl http://localhost:5000/todo2
{"todo2": "Change my brakepads"}
或者如果你安裝了 requests 庫的話,可以從 python shell 中運行:
>>> from requests import put, get
>>> put('http://localhost:5000/todo1', data={'data': 'Remember the milk'}).json()
{u'todo1': u'Remember the milk'}
>>> get('http://localhost:5000/todo1').json()
{u'todo1': u'Remember the milk'}
>>> put('http://localhost:5000/todo2', data={'data': 'Change my brakepads'}).json()
{u'todo2': u'Change my brakepads'}
>>> get('http://localhost:5000/todo2').json()
{u'todo2': u'Change my brakepads'}
Flask-RESTful 支持視圖方法多種類型的返回值。同 Flask 一樣,你可以返回任一迭代器,它將會被轉換成一個包含原始 Flask 響應對象的響應。Flask-RESTful 也支持使用多個返回值來設置響應代碼和響應頭,如下所示:
from flask import Flask, request
from flask_restful import Resource, Apiapp = Flask(__name__)
api = Api(app)todos = {}class TodoSimple(Resource):def get(self, todo_id):return {todo_id: todos[todo_id]}def put(self, todo_id):todos[todo_id] = request.form['data']return {todo_id: todos[todo_id]}class Todo1(Resource):def get(self):# Default to 200 OKreturn {'task': 'Hello world'}class Todo2(Resource):def get(self):# Set the response code to 201return {'task': 'Hello world'}, 201class Todo3(Resource):def get(self):# Set the response code to 201 and return custom headersreturn {'task': 'Hello world'}, 201, {'Etag': 'some-opaque-string'}api.add_resource(TodoSimple, '/<string:todo_id>')if __name__ == '__main__':app.run(debug=True)
測試
curl -i ?http://127.0.0.1:5000/todo1
curl -i ?http://127.0.0.1:5000/todo2
curl -i ?http://127.0.0.1:5000/todo3
端點 ( Endpoints )
很多時候,在一個 API 中,你的資源會有多個 url。可以將多個 url 傳遞給 Api 對象上的 add _ resource ()方法。每一個都將被路由到Resource
api.add_resource(HelloWorld,
? ? '/',
? ? '/hello')
你也可以為你的資源方法指定 endpoint 參數。
api.add_resource(Todo,
? ? '/todo/<int:todo_id>', endpoint='todo_ep')
示例
from flask import Flask
from flask_restful import Resource, Apiapp = Flask(__name__)
api = Api(app)class HelloWorld(Resource):def get(self):return {'hello': 'world'}class Todo(Resource):def get(self, todo_id):# Default to 200 OKreturn {'task': 'Hello world'}api.add_resource(HelloWorld, '/', '/hello')
api.add_resource(Todo, '/todo/<int:todo_id>', endpoint='todo_ep')if __name__ == '__main__':app.run(debug=True)
測試
curl ?http://127.0.0.1:5000/
curl ?http://127.0.0.1:5000/hello
curl ?http://127.0.0.1:5000/todo/1
curl ?http://127.0.0.1:5000/todo/2
參數解析
盡管 Flask 能夠簡單地訪問請求數據(比如查詢字符串或者 POST 表單編碼的數據),驗證表單數據仍然很痛苦。Flask-RESTful 內置了支持驗證請求數據,它使用了一個類似?argparse?的庫。
示例
from flask import Flask
from flask_restful import reqparse, Api, Resourceapp = Flask(__name__)
api = Api(app)parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, help='Rate to charge for this resource')class Todo(Resource):def post(self):args = parser.parse_args()print(args)# Default to 200 OKreturn {'task': 'Hello world'}api.add_resource(Todo, '/todos')if __name__ == '__main__':app.run(debug=True)
測試
curl -d 'rate=100' http://127.0.0.1:5000/todos
curl -d 'rate=foo' http://127.0.0.1:5000/todos
與 argparse 模塊不同,reqparse. RequestParser.parse _ args ()返回 Python 字典,而不是自定義數據結構。
輸入模塊提供了許多常用的轉換函數,例如 inputs.date ()和 inputs.url ()。
使用 strict = True 調用 parse _ args 可以確保在請求包含您的解析器沒有定義的參數時拋出錯誤。
args = parser.parse_args(strict=True)
curl -d 'rate2=foo' http://127.0.0.1:5000/todos
數據格式化
默認情況下,在你的返回迭代中所有字段將會原樣呈現。盡管當你剛剛處理 Python 數據結構的時候,覺得這是一個偉大的工作,但是當實際處理它們的時候,會覺得十分沮喪和枯燥。為了解決這個問題,Flask-RESTful 提供了 fields 模塊和 marshal_with() 裝飾器。類似 Django ORM 和 WTForm,你可以使用 fields 模塊來在你的響應中格式化結構。
from flask import Flask
from flask_restful import fields, marshal_with, Resource, Apiapp = Flask(__name__)
api = Api(app)resource_fields = {'task': fields.String,'uri': fields.Url('todo')
}class TodoDao(object):def __init__(self, todo_id, task):self.todo_id = todo_idself.task = task# This field will not be sent in the responseself.status = 'active'class Todo(Resource):@marshal_with(resource_fields)def get(self, **kwargs):return TodoDao(todo_id='my_todo', task='Remember the milk')api.add_resource(Todo, '/todo')if __name__ == '__main__':app.run(debug=True)
上面的例子接受一個 python 對象并準備將其序列化。marshal_with() 裝飾器將會應用到由 resource_fields 描述的轉換。從對象中提取的唯一字段是 task。fields.Url 域是一個特殊的域,它接受端點(endpoint)名稱作為參數并且在響應中為該端點生成一個 URL。許多你需要的字段類型都已經包含在內。請參閱 fields 指南獲取一個完整的列表。
$ curl ?http://127.0.0.1:5000/todo
{
? ? "task": "Remember the milk",
? ? "uri": "/todo"
}
完整?示例
from flask import Flask
from flask_restful import reqparse, abort, Api, Resourceapp = Flask(__name__)
api = Api(app)TODOS = {'todo1': {'task': 'build an API'},'todo2': {'task': '?????'},'todo3': {'task': 'profit!'},
}def abort_if_todo_doesnt_exist(todo_id):if todo_id not in TODOS:abort(404, message="Todo {} doesn't exist".format(todo_id))parser = reqparse.RequestParser()
parser.add_argument('task')# Todo
# shows a single todo item and lets you delete a todo item
class Todo(Resource):def get(self, todo_id):abort_if_todo_doesnt_exist(todo_id)return TODOS[todo_id]def delete(self, todo_id):abort_if_todo_doesnt_exist(todo_id)del TODOS[todo_id]return '', 204def put(self, todo_id):args = parser.parse_args()task = {'task': args['task']}TODOS[todo_id] = taskreturn task, 201# TodoList
# shows a list of all todos, and lets you POST to add new tasks
class TodoList(Resource):def get(self):return TODOSdef post(self):args = parser.parse_args()todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1todo_id = 'todo%i' % todo_idTODOS[todo_id] = {'task': args['task']}return TODOS[todo_id], 201##
## Actually setup the Api resource routing here
##
api.add_resource(TodoList, '/todos')
api.add_resource(Todo, '/todos/<todo_id>')if __name__ == '__main__':app.run(debug=True)
測試
curl http://localhost:5000/todos ? ? ? ?獲取列表
curl http://localhost:5000/todos/todo3 ?獲取一個單獨任務
刪除一個任務
$ curl http://localhost:5000/todos/todo2 -X DELETE -v
* ? Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 5000 failed: Connection refused
* ? Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 5000 (#0)
> DELETE /todos/todo2 HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.64.1
> Accept: */*
>?
* HTTP 1.0, assume close after body
< HTTP/1.0 204 NO CONTENT
< Content-Type: application/json
< Server: Werkzeug/1.0.1 Python/3.9.2
< Date: Sat, 06 Mar 2021 03:29:33 GMT
<?
* Closing connection 0
增加一個新的任務
$ curl http://localhost:5000/todos -d "task=something new" -X POST -v
Note: Unnecessary use of -X or --request, POST is already inferred.
* ? Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 5000 failed: Connection refused
* ? Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 5000 (#0)
> POST /todos HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Length: 18
> Content-Type: application/x-www-form-urlencoded
>?
* upload completely sent off: 18 out of 18 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 201 CREATED
< Content-Type: application/json
< Content-Length: 32
< Server: Werkzeug/1.0.1 Python/3.9.2
< Date: Sat, 06 Mar 2021 03:31:02 GMT
<?
{
? ? "task": "something new"
}
* Closing connection 0
更新一個任務
$ ?curl http://localhost:5000/todos/todo3 -d "task=something different" -X PUT -v
* ? Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 5000 failed: Connection refused
* ? Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 5000 (#0)
> PUT /todos/todo3 HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Length: 24
> Content-Type: application/x-www-form-urlencoded
>?
* upload completely sent off: 24 out of 24 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 201 CREATED
< Content-Type: application/json
< Content-Length: 38
< Server: Werkzeug/1.0.1 Python/3.9.2
< Date: Sat, 06 Mar 2021 03:32:44 GMT
<?
{
? ? "task": "something different"
}
* Closing connection 0
獲取最新列表
$ curl http://localhost:5000/todos
{
? ? "todo1": {
? ? ? ? "task": "build an API"
? ? },
? ? "todo3": {
? ? ? ? "task": "something different"
? ? },
? ? "todo4": {
? ? ? ? "task": "something new"
? ? }
}
使用 gunicorn、異步
安裝:pip install gunicorn
gunicorn 部署 flask 項目簡單示例:https://blog.csdn.net/feng_1_ying/article/details/107469379
from flask import *
from flask_restful import Api,Resource,reqparse
from gevent import monkey
from gevent.pywsgi import WSGIServermonkey.patch_all()app=Flask(__name__)
api=Api(app)class infoView(Resource):def post(self):parser = reqparse.RequestParser()parser.add_argument('username', type=str)args = parser.parse_args()return args
api.add_resource(infoView,'/info/')
if __name__ == '__main__':http_server = WSGIServer(('10.45.7.11', int(5001)), app)http_server.serve_forever()# 部署方案
# gunicorn -k gevent -b 10.45.7.11:5001 flask_restful_test:app
flask_restful.marshal 過濾 數據
示例:
from flask import Flask
from flask_restful import Api, Resource, fields, marshalapp = Flask(__name__)
api = Api(app)# 定義一個示例數據
data = {'name_1': 'John','age_1': 30,'email_address': 'john@example.com',
}# 定義字段格式和過濾器
resource_fields = {'name_1': fields.String,'name_2': fields.String,'age_2': fields.Integer,# 可以使用 attribute 指定源數據的鍵'email': fields.String(attribute='email_address')
}class HelloWorld(Resource):def get(self):# 序列化數據serialized_data = marshal(data, resource_fields)return serialized_dataapi.add_resource(HelloWorld, '/hello', '/', '/world')if __name__ == '__main__':app.run(debug=True)
flask_sqlalchemy
:https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/
Flask-SQLAlchemy 是一個與 Flask 配合使用的 SQLAlchemy 擴展,它提供了在 Flask 應用程序中使用 SQLAlchemy 進行數據庫操作的便利方法和工具。
安裝 flask_sqlalchemy 擴展:pip install flask_sqlalchemy
from flask import Flask
from flask_sqlalchemy import SQLAlchemyapp = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///mydatabase.db'
db = SQLAlchemy(app)class User(db.Model):id = db.Column(db.Integer, primary_key=True)username = db.Column(db.String(80), unique=True, nullable=False)email = db.Column(db.String(120), unique=True, nullable=False)def __repr__(self):return '<User %r>' % self.username# 創建數據表
db.create_all()# 插入數據
user = User(username='john', email='john@example.com')
db.session.add(user)
db.session.commit()# 查詢數據
users = User.query.all()
for user in users:print(user.username)# 更新數據
user = User.query.filter_by(username='john').first()
user.email = 'newemail@example.com'
db.session.commit()# 刪除數據
user = User.query.filter_by(username='john').first()
db.session.delete(user)
db.session.commit()if __name__ == '__main__':app.run(debug=True)
Flask_migrate
在開發時,以刪除表再重建的方式更新數據庫簡單直接,但明顯的缺陷是會丟掉數據庫中的所有數據。在生產環境下,沒有人想把數據都刪除掉,這時需要使用數據庫遷移工具來完成這個工作。SQLAlchemy的開發者Michael Bayer寫了一個數據庫遷移工作—Alembic來幫助我們實現數據庫的遷移,數據庫遷移工具可以在不破壞數據的情況下更新數據庫表的結構。蒸餾器(Alembic)是煉金術士最重要的工具,要學習SQL煉金術(SQLAlchemy),當然要掌握蒸餾器的使用。
擴展 Flask-Migrate 繼承了 Alembic,提供了一些 flask 命令來簡化遷移工作,可以使用它來遷移數據庫。
from flask import Flask
from flask import render_template
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrateapp = Flask(__name__)
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://user:passwd@host/my_db'
app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URIdb = SQLAlchemy(app)
Migrate(app, db)class User(db.Model):__tablename__ = 'users'id = db.Column(db.Integer, primary_key=True)email = db.Column(db.String(250), unique=True, nullable=False)username = db.Column(db.String(250), unique=True, nullable=False)password = db.Column(db.String(250), nullable=False)login_time = db.Column(db.Integer)def __init__(self, username, password, email):self.username = usernameself.password = passwordself.email = emaildef __str__(self):return "Users(id='%s')" % self.id@app.route('/')
def index():return 'Hello World'if __name__ == '__main__':app.run(debug=True)
3、總結:整體流程
Restful 運用于前后端分離
前端:app,小程序,pc頁面
后端:沒有頁面,mvt:模型模板視圖 去掉了t模板
mv:模型 視圖
模型的使用:更原來的用法相同
視圖:api構建視圖
創建 api 對象在exts擴展包中
# 在創建app的函數中綁定API 等價于 api.init_app(app=app)
api = Api(app=app)
# 在創建app的函數中綁定db ?等價于 db.init_app(app=app)
db = SQLAlchemy(api=藍圖對象) ?
定義 視圖
視圖中的類需要基礎 flask_restful 中的 Resource
from flask_restful import Resourceclass xxxApi(Resource):def get(self):passdef post(self):pass
給 app 綁定 api 視圖(view中完成)
api.add_resource(xxxApi,'/user')
格式化輸出 ( 都在view中完成 )
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_restful import fields, marshal_with, Resource, Apiapp = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///my_database.db'
api = Api(app)
db = SQLAlchemy(app)class User(db.Model):id = db.Column(db.Integer, primary_key=True)username = db.Column(db.String(80), unique=True, nullable=False)email = db.Column(db.String(120), unique=True, nullable=False)def __repr__(self):return f'<User {self.username}>'# 格式化輸出數據,相當于json的格式
user = {'id': fields.Integer,'username': fields.String,'password': fields.String
}# 定義類視圖
class UserResource(Resource):# get請求處理# 在對應的api接口上添加配置:@marshal_with(user)@marshal_with(user) # user的json格式化輸出def get(self):users = User.query.all()return usersapi.add_resource(UserResource, '/user')
路由
flask?中路由的寫法
@app.route('/user')
def user(): ----------->視圖函數.....return response對象
增加 修改 刪除 查詢 按鈕動作 都是自己在模板中寫的
回顧?flask 路由路徑上的參數解析使用:
路由的變量規則(解析路徑上的參數)
string (缺省值)接收任何不包含斜杠的文本值
int 接收正整數
float 接收正浮點數
path 類似string,但是可以接收斜杠
UUID 接收UUID字符
route規則中如果傳入了變量,那么被綁定的函數中也必須需要傳入相應的參數
解析路徑上的參數案例:
@app.route('/<int:key>') # key是一個變量,默認就是字符串類型
def city(key):return data.get(key)
restful?中路由寫法
restful:------->api-------->接口-------->資源------>uri
概述基本的 ResuFul 的建立:
class xxxApi(Response): -------視圖類
?? ?def get(self):
?? ??? ?pass
?? ? ....http://127.0.0.1:5000/user? 這個路徑可以做的事:
?? ??? ?get
?? ??? ?post
?? ??? ?put
?? ??? ?delete
?? ??? ?...
?? ??? ?
增加 ?修改 ?刪除 ?查詢 ?是通過請求方式完成的路徑產生:
api.add_resource(xxxApi,'/user')
api.add_resource(xxxApi,'/goods')
api.add_resource(xxxApi,'/order')api 解析路徑參數:http://127.0.0.1:5000/user/1
api 中的路徑參數解析(與模板中的路徑參數解析類型):
class UserGetIdResource(Resource):
?? ?@marshal_with(user)
?? ?def get(self, uid):
?? ??? ?users = User.query.get(uid)
?? ??? ?return users
api.add_resource(UserGetIdResource, '/user/<int:uid>')endpoint 的使用,便捷的反向解析出api
# 定義類視圖
class UserResource(Resource):
?? ?def put(self):
?? ??? ?print('endpoint的使用,反向解析出api:', url_for('all_user'))
?? ??? ?return {'msg': '-------->ok'}api.add_resource(UserResource, '/user', endpoint='all_user')
數據傳入(進)( request?)
參數解析:
1.使用 reqparse 對象進行傳入的參數進行解析
parser = reqparse.RequestParser() # 創建解析對象
2.使用
parser.add_argument('username', type=str, required=True, help="必須輸入賬號", location=['form'])
args = parser.parse_args()
username = args.get('username')
案例:用于api中的數據被接收,我們需要驗證或者過濾
# 參數解析
parser = reqparse.RequestParser(bundle_errors=True) ?# 解析對象
parser.add_argument('username', type=str, required=True, help="必須輸入賬號", location=['form'])
parser.add_argument('password', type=inputs.regex(r'^\d{6,12}$'), required=True, help="必須輸入密碼", location=['form'])
parser.add_argument('phone ', type=inputs.regex(r'^1[356789]\d{9}$'), location=['form'])
parser.add_argument('icon', required=True, type=FileStorage, location=['files'])
# From the request headers
parser.add_argument('Host', type=str, required=True, location=['headers'])#在對應請求中的使用
args = parser.parse_args()
username = args.get('username')
password = args.get('password')
phone = args.get('phone')
icon = args.get('icon')
host = args.get('Host')
數據返回(出)( response )
概述:return data
主要:data 必須是 json 格式,默認的返回類型不是 json 格式,并且強行轉換還會報錯。解決方法如下。數據需要返回的格式類型
? ? {
? ? ? ? 'aa':'haha',
? ? ? ? 'bb':[
? ? ? ? ? ? {
? ? ? ? ? ? 'id':1,
? ? ? ? ? ? 'xxx':[
? ? ? ? ? ? ? ? ? ? {},{},{}
? ? ? ? ? ? ? ? ]
? ? ? ? ? ? }
? ? ? ? ]
? ? }
如果直接返回不能有自定義的對象:User,Friend......
如果有,需要 marshal(),marshal_with() 幫助 JSON 的序列化進行轉換.
- 1. marshal(對象,對象的fields格式) #對象的fields格式是指字典的輸出格式 marshal([對象,對象],對象的fields格式)
- 2. marshal_with() 作為裝飾器,修飾你的請求方法
?@marshal_with(user_friend_fields)
?def get(self,id):
?? ?.....
?? ?data={
?? ??? ?xxx:xxx
?? ??? ?xxx:xxx
?? ??? ?'friends': friend_list ?# 直接是list,因為使用了@marshal_with(user_friend_fields)
?? ?}
?? ?return data
函數需要參數,參數就是最終數據輸出的格式
參數:user_friend_fields,類型:dict類型
如:
user_fields = {
?? ?'id': fields.Integer,
?? ?'username': fields.String(default='匿名'),
?? ?'pwd': fields.String(attribute='password'),
?? ?'isDelete': fields.Boolean(attribute='isdelete')
}
user_friend_fields={
?? ?'username':fields.String,
?? ?'nums':fields.Integer,
?? ?'friends':fields.List(fields.Nested(user_fields))
}
3.fields.Nested 的作用
高級類型的數據轉換
fields.Nested(fields.String) ---> ['aaa','bbb','bbbbv']
fields.Nested(user_fields) ? ---> user_fields是一個字典結構,將里邊的每一個對象轉 換成user_fields---->[user,user,user]
示例代碼:
import os
from flask import Flask
from flask import Blueprint, url_for
from flask_restful import marshal_with, marshal
from flask_restful import Resource, fields, reqparse, inputs
from flask_restful import Api
from flask_script import Manager
from werkzeug.datastructures import FileStorage
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate##############################################################
setting_conf = {}
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://user:passwd@host/my_db'
# 藍圖的別名為user,看到/api 就是我們寫的藍圖
user_bp = Blueprint('user', __name__, url_prefix='/api')
##############################################################flask_app = Flask(__name__, template_folder='../templates', static_folder='../static')
flask_app.config.from_object(setting_conf)
flask_app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URI
flask_app.register_blueprint(user_bp)# 將 SALAlchemy插件與app關聯。等價于 db.init_app(app=flask_app)
db = SQLAlchemy(flask_app)
# 將api插件與app關聯。等價于 api.init_app(app=flask_app)
api = Api(app=flask_app)
print(flask_app.url_map)class User(db.Model):id = db.Column(db.Integer, primary_key=True)username = db.Column(db.String(20))password = db.Column(db.String(15))icon = db.Column(db.String(15))phone = db.Column(db.String(11))# 格式化輸出數據,輸出的json格式如下
user = {'id': fields.Integer,'username': fields.String(20),'password': fields.String(15)
}user_fields = {'id': fields.Integer,'username': fields.String(default='匿名'),'pwd': fields.String(attribute='password'),'isDelete': fields.Boolean(attribute='isdelete')
}user_friend_fields = {'username': fields.String,'nums': fields.Integer,'friends': fields.List(fields.Nested(user_fields))
}# 參數解析
parser = reqparse.RequestParser(bundle_errors=True) # 解析對象
parser.add_argument('username', type=str, required=True, help="必須輸入賬號", location=['form'])
parser.add_argument('password', type=inputs.regex(r'^\d{6,12}$'), required=True, help="必須輸入密碼", location=['form'])
parser.add_argument('phone ', type=inputs.regex(r'^1[356789]\d{9}$'), location=['form'])
parser.add_argument('icon', required=True, type=FileStorage, location=['files'])
# From the request headers
parser.add_argument('Host', type=str, required=True, location=['headers'])# 定義類視圖
class UserResource(Resource):# get請求處理@marshal_with(user) # user的json格式化輸出def get(self):users = User.query.all()print(users)return users@marshal_with(user)def post(self):args = parser.parse_args()username = args.get('username')password = args.get('password')phone = args.get('phone')icon = args.get('icon')host = args.get('Host')print('host:', host)print('icon:', icon)# 創建user對象user_db_model = User()user_db_model.icon = iconuser_db_model.username = usernameuser_db_model.password = passworduser_db_model.phone = phonedb.session.add(user_db_model)db.session.commit()return user_db_modeldef put(self):print('endpoint的使用,反向解析出api:', url_for('all_user'))return {'msg': '-------->ok'}def delete(self):return {'msg': '-------->delete'}class UserGetIdResource(Resource):@marshal_with(user)def get(self, uid):users = User.query.get(uid)return usersdef put(self, uid):passdef post(self, uid):passdef delete(self):passclass UserFriendResoruce(Resource):@marshal_with(user_friend_fields)def get(self, id):friends = Friend.query.filter(Friend.uid == id).all()user = User.query.get(id)friend_list = []for friend in friends:u = User.query.get(friend.fid)friend_list.append(u)# data = {# 'username': user.username,# 'nums': len(friends),# 'friends': marshal(friend_list, user_fields) # marshal(數據名, 結構名)# }data = {'username': user.username,'nums': len(friends),'friends': friend_list # 直接是list,因為使用了@marshal_with(user_friend_fields)}return datadef post(self):passif __name__ == '__main__':api.add_resource(UserResource, '/user', endpoint='all_user')api.add_resource(UserGetIdResource, '/user/<int:uid>')api.add_resource(UserFriendResoruce, '/friend/<int:id>')# 搭建數據庫# migrate = Migrate(app=flask_app, db=db)pass