13-flask博客項目之restful api詳解1-概念
13-flask博客項目之restful api詳解1-概念
Flask-RESTful學習網站
英文:https://flask-restful.readthedocs.io/en/latest/
中文:http://www.pythondoc.com/Flask-RESTful/quickstart.html
一個英文一箇中文
安裝
Flask-RESTful安裝
使用 pip
安裝 Flask-RESTful:
pip install flask-restful
開發的版本可以從 GitHub 上的頁面 下載
git clone https://github.com/twilio/flask-restful.git
cd flask-restful
python setup.py develop
Flask-RESTful 有如下的依賴包(如果你使用 pip
,依賴包會自動地安裝):
- Flask 版本 0.8 或者更高
Flask-RESTful 要求 Python 版本爲 2.6, 2.7, 或者 3.3。
我這裏裝的0.3.8
postman安裝
調試程序需要用到postman
postman使用:https://blog.csdn.net/fxbin123/article/details/80428216/
一、Postman背景介紹
用戶在開發或者調試網絡程序或者是網頁B/S模式的程序的時候是需要一些方法來跟蹤網頁請求的,用戶可以使用一些網絡的監視工具比如著名的Firebug等網頁調試工具。今天給大家介紹的這款網頁調試工具不僅可以調試簡單的css、html、腳本等簡單的網頁基本信息,它還可以發送幾乎所有類型的HTTP請求!Postman在發送網絡HTTP請求方面可以說是Chrome插件類產品中的代表產品之一。
1> 、postman下載地址:
https://www.getpostman.com/apps
postman基本使用
使用它訪問一下百度
我們看下面的請求地址,請求方式
請求結果
用post發送geti請求,對這個url
上面請求200,我們還可以看請求頭
保存是將請求結果保存到一個文件中
我們隨便填個信息登錄,可以看到是發送了ajax的請求,類型是xhr。當滑動驗證的時候,也是發送了verify的ajax請求
我們可以看他登錄的請求地址和類型。我們可以看到,手機上提交的form表單都是post請求
我們可以看到表單數據攜帶郵箱密碼
第一個api
環境準備
我們這裏用的是英文的,導入的模塊路徑不同,應該是版本不同吧
from flask import Flask
from flask_restful import Resource, Api
app = 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)
我們需要將restful這個第三方組件加入到我們的項目中,它是跟db加進來是一樣的
實例化api對象
api對象初始化app
定義api藍圖,並添加api前綴
註冊藍圖
創建並修改數據庫。其它不用變
app改成這個樣子的
我們導入資源
導入之後,我們點擊resource進入查看,它就有一個dispatch_request方法
Resource類繼承了MethodView類,我們點進去看下這個類
MethodView裏面也有dispatch_request這個方法。它有繼承了使用元類。我們可以看到下面寫着要定義get post等方法
藍圖總寫一個簡單不完善的api示例
from flask import Blueprint from flask_restful import Resource, marshal_with, fields from exts import api user_bp = Blueprint('user', __name__, url_prefix='/api') from apps.user.model import User user_fields = { 'id': fields.Integer, 'username': fields.String, 'password': fields.String, 'udatetime': fields.DateTime } # 定義類視圖 class UserResource(Resource): # get 請求的處理 @marshal_with(user_fields) def get(self): users = User.query.all() # userList = [] # for user in users: # userList.append(user.__dict__) return users # post def post(self): return {'msg': '------>post'} # put def put(self): return {'msg': '------>put'} # delete def delete(self): return {'msg': '------>delete'} api.add_resource(UserResource, '/user')
我們在藍圖中如下操作:
從組件中導入資源,定義視圖類,然後讓它繼承資源類,在自己定義的視圖類下面定義了get ,post,put,delete四個方法,分別對於查增改刪四個功能。
給我們的視圖類添加路由,通過調用之前在exts目錄init文件中從組件導入的api類生成的api對象,然後api對象點增加資源方法 。將我們定義的視圖類加進去,後面是路由字符串。這樣就給我們定義的視圖類增添了路由映射了。
from flask import Blueprint from flask_restful import Resource from exts import api user_bp = Blueprint('user', __name__, url_prefix='/api') # 定義類視圖 class UserResource(Resource): # get 請求的處理 def get(self): return {'msg': '------>get'} # post def post(self): return {'msg': '------>post'} # put def put(self): return {'msg': '------>put'} # delete def delete(self): return {'msg': '------>delete'} api.add_resource(UserResource, '/user')
啓動程序,我們從瀏覽器訪問一下,當我們get請求路由的時候,找的這個視圖類,然後找到get請求,執行get方法,響應這個字典。前端接收到並在瀏覽器頁面顯示粗來。這裏添加了api前綴,但是這裏不需要添加api前綴,就能訪問到這裏的視圖類,如果添加上了反而無法訪問到,可能是因爲沒有其它藍圖中有相同的路由的原因吧,所以用不上
我們在postman中也是可以訪問
我們四個請求方式都使用了,這裏目前都是可以請求到對應方式的t數據
當請求一個未在視圖類中定義的請求方法時,報錯
我們先定義一個模型,然後遷移數據
添加兩個用戶,
我們在用戶查詢方法裏面查出這個用戶對象列表,然後返回這個用戶對象列表。再看postman請求
此時,不能直接返回用戶對象列表。報錯沒有json序列化
我們改成這樣還是不行
這樣也不行
官網是這樣做的
當我們添加marshal_with裝飾器,裝飾器傳參用戶字典,用戶字段字典裏面定義用戶表表中有的字段,每個字段的類型是什麼,這樣再遇到執行視圖裝飾器裝飾下的get方法時,返回用戶對象,就能將用戶對象的信息像用戶字段字典定義的格式查出來並響應給客戶端。這裏只響應一個用戶對象,返回的是個字典。
當我去掉所以,返回用戶對象列表後,響應結果是每一個用戶對象一個字典,所有用戶對象字典組成一個用戶對象列表。每個用戶對象返回的信息都是安裝用戶字段設置的格式返回的字段數據,從表中查出數據。這裏字段名字和表字段名字保持一致的。字段類型也需要設置。從數據庫中查詢出的數據,會渲染到這些相同字段名稱下。也就是。用戶字段前面自己定義,後面應該必須是_fields命名。然後需要返回數據庫對象的方法前面添加marshal_with裝飾器,並把要響應的格式用戶字段傳進去,這樣裝飾器中會幫我們把用戶對象列表中的每個用戶對象按照user_fiels來生成響應數據。從而生成響應數據列表。只有一個用戶對象,請求結果是一個字典,有多個用戶時就是多個字典在一個列表裏面
from flask import Blueprint from flask_restful import Resource, marshal_with, fields from exts import api user_bp = Blueprint('user', __name__, url_prefix='/api') from apps.user.model import User user_fields = { 'id': fields.Integer, 'username': fields.String, 'password': fields.String, 'udatetime': fields.DateTime } # 定義類視圖 class UserResource(Resource): # get 請求的處理 @marshal_with(user_fields) def get(self): users = User.query.all() # userList = [] # for user in users: # userList.append(user.__dict__) return users # post def post(self): return {'msg': '------>post'} # put def put(self): return {'msg': '------>put'} # delete def delete(self): return {'msg': '------>delete'} api.add_resource(UserResource, '/user')
如果在字段裏面只定義了一個,那麼get獲取的數據也只有這一個。不會因爲你響應的數據的字段多而變多。我們的數據庫字段是比較多的,當我們想要只返回一兩個字段時,那麼只需要在xxfields裏面定義指定要返回的字段就可以。
總結
什麼是RESTful架構: (1)每一個URI代表一種資源; (2)客戶端和服務器之間,傳遞這種資源的某種表現層; (3)客戶端通過四個HTTP動詞(GET,POST,PUT,DELETE,[PATCH]),對服務器端資源進行操作,實現"表現層狀態轉化"。 Postman 前後端分離: 前端: app,小程序,pc頁面 後端: 沒有頁面,mtv: 模型模板視圖 去掉了t模板。 mv:模型 視圖 模型的使用:跟原來的用法相同 視圖: api構建視圖 步驟: 1. pip3 install flask-restful 2.創建api對象 api = Api(app=app) api = Api(app=藍圖對象) 3. 定義類視圖: from flask_restful import Resource class xxxApi(Resource): def get(self): pass def post(self): pass def put(self): pass def delete(self): pass 4. 綁定 api.add_resource(xxxApi,'/user')
flask restful官網目錄彙總
中文的可以用來學習,可能包路徑是不一樣的,跟中文的
restful api資源路由
上面寫了一個接口,這個接口返回的就是一個json數據,提供前端請求使用,
一個視圖函數,按照繼承的類是資源,我們可以稱之爲一個視圖函數就是一個資源吧,而視圖函數的請求路徑的字符串映射,也就是路由,按官網翻譯就是資源路由
jquery使用地址(ajax使用地址) :https://jquery.cuishifeng.cn/
我們在前面的基礎上再添加一個類視圖。上面哪個是對所有用戶的操作,下面這個是對單個用戶的操作,對單個用戶的操作需要指定用戶的id。也就是說當是同一張表時,我們或許可以根據前端需求,給同一個表添加多個不同的類視圖,以返回不同的響應數據。這裏就是所有用戶和單個用戶是分開的,也就是還要單獨添加路由,這個路由是需要傳參的。至於是否可以合併 ,看情況考慮。
我們需要添加單個用戶的類視圖,添加單個用戶的增刪改查四個功能,添加單個用戶的類視圖的訪問路由。需要將單個 用戶資源類視圖作爲添加路由的參數,然後後面填寫映射的路徑字符串。因爲單個用戶是需要傳遞用戶id參數的,這裏可以使用這種方式傳參,但是類視圖中每個方法需要定義接收這個傳參的形參的
我們根據傳進的用戶id,將用戶對象查出來然後響應回去,但是我們需要的響應的數據需要是json格式的。
因此我們需要給它添加marshal,將用戶對象轉成一個序列號對象,以之前定義的用戶字段格式來響應。
然後 我們測試。可以發現,當我們不接參數的時候,訪問的是上面返回所有用戶的
當我們接上參數的時候,返回的是單個用戶的
from flask import Blueprint from flask_restful import Resource, marshal_with, fields from exts import api user_bp = Blueprint('user', __name__, url_prefix='/api') from apps.user.model import User user_fields = { 'id': fields.Integer, 'username': fields.String, 'password': fields.String, 'udatetime': fields.DateTime } # 定義類視圖 class UserResource(Resource): # get 請求的處理 @marshal_with(user_fields) def get(self): users = User.query.all() # userList = [] # for user in users: # userList.append(user.__dict__) return users # post def post(self): return {'msg': '------>post'} # put def put(self): return {'msg': '------>put'} # delete def delete(self): return {'msg': '------>delete'} class UserSimpleResource(Resource): @marshal_with(user_fields) # user轉成一個序列化對象, def get(self, id): user = User.query.get(id) return user # 不是str,list,int,。。。 def put(self, id): pass def delete(self, id): pass api.add_resource(UserResource, '/user') api.add_resource(UserSimpleResource, '/user/<int:id>')
一個網址,後端是同樣的,但是前端可以有不同的,如pc端,小程序,手機app等等。前端需要什麼數據,後端需要前端傳什麼參數,需要前後端開發人員協商好
我們除了上面那種傳參,還可以用問號方式傳參
endpoints
我們在這裏打印一下url_map,就能打印出路由的信息
當我們請求一條路由的時候,就打印出路由信息
當我們不添加endpoint時,它用的時視圖類名稱小寫做的endpoint,
當我們添加了endpoint之後,就叫我們修改後的那個名稱,它指代的就是那條路由,可以使用endpoint來做反向解析
我們在單個用戶的視圖類put方法下打印一下all_user這個endpoint的反向解析,
postman請求正常
然後我們可以看到通過endpoint名稱,可以解析出它對應的路由。也就是在別的地方,我們需要使用這個路由,就可以使用endpoint去做反向解析出它的路由來了
參數解析
from flask import Blueprint, url_for from flask_restful import Resource, marshal_with, fields,reqparse from exts import api user_bp = Blueprint('user', __name__, url_prefix='/api') from apps.user.model import User from exts import db user_fields = { 'id': fields.Integer, 'username': fields.String, 'password': fields.String, 'udatetime': fields.DateTime } # 參數解析 parser = reqparse.RequestParser() # 解析對象 parser.add_argument('username', type=str, required=True, help='必須輸入用戶名') parser.add_argument('password', type=str , required=True, help='必須輸入密碼', location=['form']) parser.add_argument('phone', type=str) # 定義類視圖 class UserResource(Resource): # get 請求的處理 @marshal_with(user_fields) def get(self): users = User.query.all() # userList = [] # for user in users: # userList.append(user.__dict__) return users @marshal_with(user_fields) def post(self): # 獲取數據 args = parser.parse_args() username = args.get('username') password = args.get('password') phone = args.get('phone') # 創建user對象 user = User() user.username = username user.password = password if phone: user.phone = phone db.session.add(user) db.session.commit() return user # put def put(self): return {'msg': '------>put'} # delete def delete(self): return {'msg': '------>delete'} class UserSimpleResource(Resource): @marshal_with(user_fields) # user轉成一個序列化對象, def get(self, id): user = User.query.get(id) return user # 不是str,list,int,。。。 def put(self, id): print('endpoint的使用:', url_for('all_user')) return {'msg': 'ok'} def delete(self, id): pass api.add_resource(UserResource, '/user',endpoint='all_user') api.add_resource(UserSimpleResource, '/user/<int:id>')
我們想要請求這個路徑的時候這個方式去攜帶很多個值
我們提交數據時帶着數據
需要用到
我們在用戶字段下面創建解析對象,然後添加參數。我們可以對前端的傳參進行解析,進行校驗。相當於form類的作用
我們可以看到,添加參數是請求解析類中的一個方法。
我們看下請求解析這個類的init方法,裏面有些參數,我們沒有設置的時候用的是這些默認參數。trim就是默認不做空字符的去除,bundle_errors就是,如果enabled,當第一個錯誤出來的時候,不會中止。繼續往下校驗所有的字段
給解析器添加參數
我們按照下面添加參數,如果前端有電話,後端沒有電話參數,是不可以的,是不能多給的
添加參數,限制傳參的類型,指明傳參類型必須是整型。默認是字符串類型
比如我們的分頁,必須是整型,這時就需要前端傳遞過來的必須是整型,所以我們可以像上面那樣添加數據類型必須是整型才能成功提交,才能校驗通過
我們給模型添加一個電話字段,遷移數據
然後添加參數,指定參數名稱,類型,必填,幫助信息,就是報錯信息
我們寫好之後,前端就可以傳數據了。後端視圖函數中需要取值,就從parse_args裏面取
我們導入reqpares和數據庫
我們實例化解析對象,添加三個參數
當前端發送post請求,將三個數據傳遞到後端,我們在post中從解析器裏面取數據
需要先調用解析參數方法,然後從這個對象中獲取請求過來的form數據,將他們保存到數據庫,再將這個對象返回給前端,因爲需要返回給前端,所以需要是json序列化過的,需要添加上marshal_with裝飾器。指定按照用戶字段這個格式返回數據
我們在postman中添加數據,如果是文本的就用這個就行,如果是有文件的,選擇form-data。這裏是post請求,我們應該在body裏面添加數據,而不是在params裏面添加數據。這裏添加鍵值對,就相當於你在form表單裏面寫入了數據。
當我們輸入鍵值對,點擊發送post請求之後。我們從解析器中獲取到用戶傳過來的鍵值對,然後保存到數據庫中,數據庫中已經增加了這條數據了。post方法裏面返回這個添加的用戶,添加裝飾器和指定返回的字段格式後,我們在postman中就接收到了保存下來的數據信息。
我們在params裏面添加鍵值對,這樣來發送post請求。也是可以接收到數據的,從而往下執行保存進入數據庫。畢竟有的地方form表單post請求就是可以接問號拼接傳參的,只要取得鍵值對對應上就行
我們在location參數裏面添加form時,這樣就限制了,表示form提交的數據裏必須有這些字段,而我們是從params裏面寫的,那麼相當於每天在body裏面填寫form數據,所以請求到達解析器就對數據做了校驗,相當於沒有填寫username,就把錯誤信息返回給請求客戶端了。這樣前端可以根據這個判斷,如果有錯誤信息就在前端渲染,否則就是用用戶信息做啥渲染的。或者其它操作等等
我們導入input,裏面使用正則校驗,這樣沒有通過校驗的字段,就會將幫助信息,也就是未校驗通過的錯誤信息響應給客戶端。其它字段,包括密碼我們都可以設置校驗。除了正則校驗還可以使用其它校驗方法。密碼的正則校驗,可以通過正則限制個數,這裏是6到12位,我們提示信息也修改完整一點,提示是需要6到12位的數字
我們將手機號讓它通過校驗,我們可以看到能成功提交數據。
我們再get請求5號用戶,將body鍵值對取消勾選,然後點擊發送。我們就能得到5號用戶的數據
類似於複選框功能的數據添加
複選框,我們需要添加action 是append
在post方法中我們get這個愛好。打印一下,這裏沒有添加數據庫這個字段,只是看一下請求時這裏接收的數據是怎樣的
我們發送post請求,添加上多個值,鍵是一樣的。
我們解析器定義接收的字段行爲是追加,所以我們從參數解析器裏面取的這個字段,是postman裏面添加的多個值組成的一個列表。
換名字
前端location 文件上傳,頭像上傳
添加頭像字段,遷移數據
location有多種類型,
如果是文件上傳,類型必須得填文件存儲,
他們就對應我們從不同裏面取值是一樣的。像下面電話裏面,是可以填寫多個location的,這樣支持多種數據提交
看下文件存儲類,它裏面也是用了之前我們使用的存儲驗證碼圖片用的字節io對象。
因爲我們需要上傳文件了,所以需要將數據修改位formdata。我們點擊後面bulk edit
複製粘貼鍵值對到form-data中
再點擊一下,粘貼到formdata裏面鍵值對編輯。
這樣就將鍵值對複製過來了
選中所有,點擊塊編輯
可以看到,沒有雙斜線//了,這說明沒有選中使用的鍵值對是//這種註釋,沒有註釋//的就是被選中需要使用的鍵值對。每個鍵值對都是冒號隔開,多個鍵值對換行分隔,這樣我們就能批量添加,修改和刪除鍵值對了。所以這個地方叫塊編輯。而返回到之前的狀態就是鍵值對編輯。描述信息在這裏是怎麼定義的一會看,我試了一下,鍵值對描述信息在塊編輯裏面不顯示
我們添加文件存儲參數,
from werkzeug.datastructures import FileStorage
parser.add_argument('icon', type=FileStorage, location=['files'])
post請求裏面再獲取頭像文件
我們在後端post請求邏輯中,根據頭像字段名稱是從參數解析器裏面獲取頭像文件,這是個文件存儲對象。如果或者到頭像文件對象,那麼就保存在服務器上,然後將圖片的相對路徑寫入到數據庫中。
而前端是用postman,使用form data方式添加字段,發送post請求。將key修改爲file類型。然後點擊value裏就可以將文件加進來,點擊發送,就會請求到後端。最終走到post請求這個方法裏,然後保存文件,保存文件路徑到數據庫,並返回這個用戶對象的信息,按照之前定好的用戶對象字段格式。
因爲是按照這裏定義的格式返回的數據,所以並沒有返回圖片字段的信息
參數繼承,新建一個繼承其它寫好的參數解析器,然後在原基礎上做增刪改等
參數繼承
from flask_restful import reqparse parser = reqparse.RequestParser() parser.add_argument('foo', type=int) parser_copy = parser.copy() parser_copy.add_argument('bar', type=int) # parser_copy has both 'foo' and 'bar' parser_copy.replace_argument('foo', required=True, location='json') # 'foo' is now a required str located in json, not an int as defined # by original parser parser_copy.remove_argument('foo') # parser_copy no longer has 'foo' argument
錯誤信息處理
如果添加bundle_errors=True,那麼響應數據,會將所有字段都校驗完,將校驗結果返回給客戶端。而如果沒有添加的話,那麼只會返回第一個校驗失敗的消息,其它錯誤消息不會返回,甚至可能都沒有往下進行校驗
from flask_restful import reqparse parser = reqparse.RequestParser(bundle_errors=True) parser.add_argument('foo', type=int, required=True) parser.add_argument('bar', type=int, required=True) # If a request comes in not containing both 'foo' and 'bar', the error that # will come back will look something like this. { "message": { "foo": "foo error message", "bar": "bar error message" } } # The default behavior would only return the first error parser = RequestParser() parser.add_argument('foo', type=int, required=True) parser.add_argument('bar', type=int, required=True) { "message": { "foo": "foo error message" } }
錯誤消息
from flask_restful import reqparse parser = reqparse.RequestParser() parser.add_argument( 'foo', choices=('one', 'two'), help='Bad choice: {error_msg}' ) # If a request comes in with a value of "three" for `foo`: { "message": { "foo": "Bad choice: three is not a valid choice", } }
輸出字段
輸出字段可以設置複雜結構
輸出字段
Flask-RESTful 提供了一個簡單的方式來控制在你的響應中實際呈現什麼數據。使用 fields
模塊,你可以使用在你的資源裏的任意對象(ORM 模型、定製的類等等)並且 fields
讓你格式化和過濾響應,因此您不必擔心暴露內部數據結構。
當查詢你的代碼的時候,哪些數據會被呈現以及它們如何被格式化是很清楚的。
也就是我在這裏減少一個字段,那麼用戶就看不到這個字段,如果添加上那麼用戶就能看到這個字段
基本用法
你可以定義一個字典或者 fields
的 OrderedDict 類型,OrderedDict 類型是指鍵名是要呈現的對象的屬性或鍵的名稱,鍵值是一個類,該類格式化和返回的該字段的值。這個例子有三個字段,兩個是字符串(Strings)以及一個是日期時間(DateTime),格式爲 RFC 822 日期字符串(同樣也支持 ISO 8601)
from flask_restful import Resource, fields, marshal_with resource_fields = { 'name': fields.String, 'address': fields.String, 'date_updated': fields.DateTime(dt_format='rfc822'), } class Todo(Resource): @marshal_with(resource_fields, envelope='resource') def get(self, **kwargs): return db_get_todo() # Some function that queries the db
這個例子假設你有一個自定義的數據庫對象(todo
),它具有屬性:name
, address
, 以及 date_updated
。該對象上任何其它的屬性可以被認爲是私有的不會在輸出中呈現出來。一個可選的 envelope
關鍵字參數被指定爲封裝結果輸出。
裝飾器 marshal_with
是真正接受你的數據對象並且過濾字段。marshal_with
能夠在單個對象,字典,或者列表對象上工作。
注意:marshal_with 是一個很便捷的裝飾器,在功能上等效於如下的 return marshal(db_get_todo(), resource_fields), 200
。這個明確的表達式能用於返回 200 以及其它的 HTTP 狀態碼作爲成功響應(錯誤響應見 abort
)。
給字段多加個括號,裏面加上東西,看着沒區別啊
重命名屬性
很多時候你面向公衆的字段名稱是不同於內部的屬性名。使用 attribute
可以配置這種映射。
fields = { 'name': fields.String(attribute='private_name'), 'address': fields.String, }
沒添加之前
添加之後
點進去
string和integer這些繼承Raw
raw裏面添加了attribute了,還有個默認,那麼我們添加一個默認
這樣實現了字段值的隱匿性。也實現了重命名字段值
我們也可以修改前端展示的字段名字。上面是沒有修改,字段值跟模型的字段值名字是一樣,所以前端展示的也是跟模型的字段值一樣是username;下面我們就讓前端看到的和模型字段名字不同,這樣客戶端請求後就不能獲取到我們數據庫真實的字段名稱了,數據庫也相對更安全,做法如下:用戶字段這裏修改爲前端能看到的字段名稱private_name,然後在字符串對象裏面添加attribute屬性,屬性值使用這個字段對應的模型中字段的名稱,這樣將二者關聯起來,後面數據就會使用這個字段的數據庫的值了,後面還有個默認值,也就是沒有值的話,就會用到這個默認值。
我們看了兩個字段類型都是繼承Raw這個類,沒看過是否所有都繼承Raw,不過猜測大部分都是繼承Raw的,那麼繼承了的就會都可以添加attribute 和default兩個參數,那麼它們就都可以修改給前端的展示字段,隱匿數據庫字段名,給請求客戶端展示的都是我這裏定義的字段值,而非數據庫字段值。也就是前端看到的不一定就是數據庫字段名
如下,如果不適應attribute來修改前端顯示字段名,那麼字段名默認是藥和數據庫字段名是保持一致的。不然是找不到數據的,找不到就顯示null了
總結:
1.需要定義字典,字典的格式就是給客戶端看的格式
user_fields = {
'id': fields.Integer,
'username': fields.String(default='匿名'),
'pwd': fields.String(attribute='password'),
'udatetime': fields.DateTime(dt_format='rfc822')
}
客戶端能看到的是: id,username,pwd,udatetime這四個key
默認key的名字是跟model中的模型屬性名一致,如果不想讓前端看到命名,則可以修改
但是必須結合attribute='模型的字段名'
lambda 也能在 attribute
中使用
fields = { 'name': fields.String(attribute=lambda x: x._private_name), 'address': fields.String, }
也可以使用屬性訪問嵌套屬性
fields = { 'name': fields.String(attribute='people_list.0.person_dictionary.name'), 'address': fields.String, }
默認值
如果由於某種原因你的數據對象中並沒有你定義的字段列表中的屬性,你可以指定一個默認值而不是返回 None
。
fields = { 'name': fields.String(default='Anonymous User'), 'address': fields.String, }
自定義字段&多個值
有時候你有你自己定義格式的需求。你可以繼承 fields.Raw
類並且實現格式化函數。當一個屬性存儲多條信息的時候是特別有用的。例如,一個位域(bit-field)各位代表不同的值。你可以使用 fields
複用一個單一的屬性到多個輸出值(一個屬性在不同情況下輸出不同的結果)。
這個例子假設在 flags
屬性的第一位標誌着一個“正常”或者“迫切”項,第二位標誌着“讀”與“未讀”。這些項可能很容易存儲在一個位字段,但是可讀性不高。轉換它們使得具有良好的可讀性是很容易的。
自定義字段輸出,需要繼承Raw,然後重寫format方法
class UrgentItem(fields.Raw): def format(self, value): return "Urgent" if value & 0x01 else "Normal" class UnreadItem(fields.Raw): def format(self, value): return "Unread" if value & 0x02 else "Read" fields = { 'name': fields.String, 'priority': UrgentItem(attribute='flags'), 'status': UnreadItem(attribute='flags'), }
我們在模型裏面添加一個布爾類型的數據
我們給1 5 7 設置爲已經被刪除的,其它默認就是未刪除就是0狀態
我們添加一個查詢時將這個字段展示給前端。我們可以看到數據庫中存儲的是布爾值類型的,是0和1數據。客戶端請求時時顯示true和false。這裏給前端展示的是用大寫的D。我們想要讓前端展示的內容根據數據庫值不同顯示其它的可讀信息。0展示未刪除,1展示已刪除。這樣我們需要添加一個判斷
我們如官網樣例,添加判斷。
所以我們可以自己定義輸出字段 。我們前面用的string和integer這些字段都是繼承Raw類的,我們自己創建一個自定義輸出字段,也需要繼承Raw類,然後重寫format方法,裏面放一個value形參。然後在輸出字段字典裏添加一個展示給客戶的字段,也是讓它關聯上isdelete模型字段。但是用的不是組件裏面的輸出字段類,而是我們自己寫的isDelete類,因爲它繼承Raw類,所以我們也可以傳參屬性attribute,讓我們定義的能跟isdelete模型字段關聯起來。
再看看我們定義的輸出字段類,裏面的value就是模型中關聯字段的值,存入的是0 1 布爾類型,所以打印出來是true和false,我們做了個判斷,如果true返回什麼字符串,否則返回什麼字符串,這樣就將輸出修改掉了。數據庫中的值是固定的兩個,這裏就能根據值來返回我們想要給前端展示的可讀性好的一個字符串值。返回內容定製化
Url & 其它具體字段
- 用於xx列表展示頁和某個xx詳情頁
- 需要定義兩個視圖類,一個是所有xx,響應所有xx字段格式,裏面不包含xx所有字段,但包含每個xx的詳情頁訪問地址,使用詳情頁路由的endpoint
- 另一個是單個xx詳情視圖類,路由和方法上都要有單個xx的模型id,路由上傳的id要和模型id名稱一致
Flask-RESTful 包含一個特別的字段,fields.Url
,即爲所請求的資源合成一個 uri。這也是一個好示例,它展示瞭如何添加並不真正在你的數據對象中存在的數據到你的響應中。
class RandomNumber(fields.Raw): def output(self, key, obj): return random.random() fields = { 'name': fields.String, # todo_resource is the endpoint name when you called api.add_resource() 'uri': fields.Url('todo_resource'), 'random': RandomNumber, }
默認情況下,fields.Url
返回一個相對的 uri。爲了生成包含協議(scheme),主機名以及端口的絕對 uri,需要在字段聲明的時候傳入 absolute=True
。傳入 scheme
關鍵字參數可以覆蓋默認的協議(scheme):
fields = { 'uri': fields.Url('todo_resource', absolute=True) 'https_uri': fields.Url('todo_resource', absolute=True, scheme='https') }
就是生成一個訪問地址,點擊一下就能夠訪問它
from flask import Blueprint, url_for from flask_restful import Resource, marshal_with, fields,reqparse,inputs from exts import api from werkzeug.datastructures import FileStorage from settings import Config import os user_bp = Blueprint('user', __name__, url_prefix='/api') from apps.user.model import User from exts import db class IsDelete(fields.Raw): def format(self, value): print('------------------>', value) return '刪除' if value else '未刪除' user_fields_1 = { 'id': fields.Integer, 'username': fields.String(default='匿名'), 'uri': fields.Url('single_user', absolute=True) } user_fields = { 'id': fields.Integer, 'private_name': fields.String(attribute='username',default='匿名'), 'password': fields.String, 'isDelete': fields.Boolean(attribute='isdelete'), 'isDelete1': IsDelete(attribute='isdelete'), 'udatetime': fields.DateTime(dt_format='rfc822') } # 參數解析 parser = reqparse.RequestParser(bundle_errors=True) # 解析對象 # a783789893hf request.form.get() | request.args.get() | request.cookies.get() | request.headers.get() 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='必須輸入6~12位數字密碼', location=['form']) parser.add_argument('phone', type=inputs.regex(r'^1[356789]\d{9}$'), location=['form'], help='手機號碼格式錯誤') parser.add_argument('hobby', action='append') # ['籃球', '遊戲', '旅遊'] parser.add_argument('icon', type=FileStorage, location=['files']) # 定義類視圖 class UserResource(Resource): # get 請求的處理 @marshal_with(user_fields_1) def get(self): users = User.query.all() # userList = [] # for user in users: # userList.append(user.__dict__) return users @marshal_with(user_fields) def post(self): # 獲取數據 args = parser.parse_args() username = args.get('username') password = args.get('password') phone = args.get('phone') bobby = args.get('hobby') print(bobby) icon = args.get('icon') print(icon) # 創建user對象 user = User() user.username = username user.password = password if icon: upload_path = os.path.join(Config.UPLOAD_ICON_DIR, icon.filename) icon.save(upload_path) # 保存路徑個 user.icon = os.path.join('upload/icon', icon.filename) if phone: user.phone = phone db.session.add(user) db.session.commit() return user # put def put(self): return {'msg': '------>put'} # delete def delete(self): return {'msg': '------>delete'} class UserSimpleResource(Resource): @marshal_with(user_fields) # user轉成一個序列化對象, def get(self, id): user = User.query.get(id) return user # 不是str,list,int,。。。 def put(self, id): print('endpoint的使用:', url_for('all_user')) return {'msg': 'ok'} def delete(self, id): pass api.add_resource(UserResource, '/user',endpoint='all_user') api.add_resource(UserSimpleResource, '/user/<int:id>', endpoint='single_user')
我們添加一個展示字段的字典,前面的是展示所有用戶的,但是不展示用戶所有字段,指包含用戶名和每個用戶詳情訪問地址,點擊用戶訪問地址就能看到該用戶的詳情,所有字段;後面的給前端展示單個用戶的,也就是用戶詳情,
查看所有用戶和單個用戶的視圖類的get,要和對應字段響應格式綁定上。
用戶詳情的要傳用戶id。傳參字段名要和模型中用戶id字段名保持一致,之前使用uid但是報錯了,名稱對不上和模型中,這個id也可能是響應字段字典中對應的字段名,也就是給前端看的那個字段名,後面測試。傳參進去,才能查出並返回單個用戶對象
Url輸出字段,第一個參數填字符串,是放用戶詳情的endpoint。這裏使用absolute
網頁測試:
訪問所有用戶,響應所有用戶的,生成單個用戶的訪問地址。這個地址如何生成的呢?根據反向解析找的路徑,然後根據傳參id,在這裏的id還是在模型裏的id添加上的數字
點擊id是1的地址打開一個postman請求標籤頁,就是用戶1的訪問地址,點擊發送
成爲走單個用戶,走響應詳情的字段格式。這樣的話,像首頁博客列表,每個博客詳情頁需要地址 ;商品列表,每個商品詳情頁需要地址;產品列表,每個產品詳情頁需要地址等等類似的,都可以這樣來做
當我們將字段改爲uid時
fields.Url根據endpoint反向解析路徑,會找不到uid,因此路由添加的用戶id傳參和視圖名稱要和model裏面用戶id名稱要一致纔行。model裏面是id,這裏傳參也是用id才能根據名字相同,在fields.Url反向解析時,將用戶id拼接到url訪問地址上,成爲用戶詳情訪問地址
複雜結構以及第一第二張表是同一張表的表結構設計
你可以有一個扁平的結構,marshal_with 將會把它轉變爲一個嵌套結構
>>> from flask_restful import fields, marshal >>> import json >>> >>> resource_fields = {'name': fields.String} >>> resource_fields['address'] = {} >>> resource_fields['address']['line 1'] = fields.String(attribute='addr1') >>> resource_fields['address']['line 2'] = fields.String(attribute='addr2') >>> resource_fields['address']['city'] = fields.String >>> resource_fields['address']['state'] = fields.String >>> resource_fields['address']['zip'] = fields.String >>> data = {'name': 'bob', 'addr1': '123 fake street', 'addr2': '', 'city': 'New York', 'state': 'NY', 'zip': '10468'} >>> json.dumps(marshal(data, resource_fields)) '{"name": "bob", "address": {"line 1": "123 fake street", "line 2": "", "state": "NY", "zip": "10468", "city": "New York"}}'
注意:address 字段並不真正地存在於數據對象中,但是任何一個子字段(sub-fields)可以直接地訪問對象的屬性,就像沒有嵌套一樣。
我們根據用戶表構建一個朋友表,用戶表是第一張表,第二張表還是用戶表,這個朋友表相當於第三張表。 因爲朋友也是用戶,朋友的信息也是在用戶表裏面,只是朋友表裏面存放的使用某個用戶和其它用戶的關係映射,是同一張表不同行之間的關係映射。這個應該是可以做成一對一和一對多 多對多的關係的,只要設定好下面那張表的外鍵uid和Fid是否是唯一鍵就可以實現。
在原有用戶表上,添加朋友表和關係映射對象,遷移數據
我們給朋友表添加數據
我們添加用戶朋友表的路由和視圖類,這裏只是查詢,就只寫get就好了
我們再看下用戶朋友視圖類,我們要查用戶朋友,需要知道是查哪個用戶的朋友,所以路由上需要添加上用戶的id。視圖類方法中需要接收這個用戶id參數,因爲用戶的朋友還是用戶,所以我們根據用戶id從用戶表中查到該用戶的朋友列表。然後構造數據結構響應給請求客戶端。需要響應的數據結構如下:要顯示是哪個用戶,有多少個朋友,然後是朋友列表裏面每個朋友的信息,每個朋友就是一個用戶詳情的信息 。然後將這個數據結構返回給客戶端。那麼restful api中想要響應這種複雜的數據結構,是需要如何實現呢
那麼朋友列表需要怎麼做呢?friends是我們根據傳進來的用戶id在朋友表中查詢來的該用戶對應的所有朋友的id。但是我們需要給客戶端傳朋友的詳情而不是朋友的id,所以需要根據所有朋友的id在用戶表中找的所有朋友的用戶對象。這裏是遍歷朋友id列表,然後在用戶表中查出每個朋友對象並追加到朋友列表中。這樣我們就有了朋友對象列表了,而返回客戶端詳情信息,我們一般都是通過傳返回對象,然後根據輸出字段格式將對象的所有字段顯示給客戶端的
這時我們請求一下試試,我們可以看到報錯外鍵錯誤
在後端報錯 foreign_keys,需要個外鍵參數
File "D:\softwareinstall\python3\lib\site-packages\sqlalchemy\util\compat.py", line 208, in raise_ raise exception sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship User.friends - there are multiple foreign key paths linking the tables. Specify th e 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.
我們添加到這裏試試
還是會報錯
我們把朋友列表先註釋試試
還是會報錯
將關係映射字段先註釋掉
get這裏返回的是個字典,沒有model對象。沒有model對象的時候沒有使用裝飾器也是可以正常請求到的。如果有model對象返回,是需要添加裝飾器然後綁定輸出字段格式,那麼這裏需要怎麼做呢,這裏其它的已經正常輸出給客戶端了,我們需要將朋友對象列表也返回給客戶端
如果我們直接返回朋友對象列表,是沒有序列化的。
我們可以使用marshal加工一下。將model對象列表放到marshl中,然後指定列表中每個對象需要按照哪個輸出字段格式usre_fields展示給客戶端。這樣就會將包含model對象的數據按指定格式去展示給客戶端了。如果這裏有多個model對象展示,那麼我們可以構造含有多個model對象的數據結構,將每個model對象或對象列表都用一下marshal,並且定義好對象的返回格式。這裏不能用marshal_with這個裝飾器,因爲marshal_with雖然能序列化model對象或者對象列表,但是不能格式化其它數據,這裏的數據還包含了username,nums等等。要使用裝飾器的方式需要另外做處理,後面講
from flask_restful import marshal
'friends': marshal(friend_list,user_fields)
我們用頁面訪問一下。正是我們需要的樣式
列表字段
你也可以把字段解組(unmarshal)成列表
>>> from flask_restful import fields, marshal >>> import json >>> >>> resource_fields = {'name': fields.String, 'first_names': fields.List(fields.String)} >>> data = {'name': 'Bougnazal', 'first_names' : ['Emile', 'Raoul']} >>> json.dumps(marshal(data, resource_fields)) >>> '{"first_names": ["Emile", "Raoul"], "name": "Bougnazal"}'
高級:嵌套字段
儘管使用字典套入字段能夠使得一個扁平的數據對象變成一個嵌套的響應,你可以使用 Nested
解組(unmarshal)嵌套數據結構並且合適地呈現它們。
>>> from flask_restful import fields, marshal >>> import json >>> >>> address_fields = {} >>> address_fields['line 1'] = fields.String(attribute='addr1') >>> address_fields['line 2'] = fields.String(attribute='addr2') >>> address_fields['city'] = fields.String(attribute='city') >>> address_fields['state'] = fields.String(attribute='state') >>> address_fields['zip'] = fields.String(attribute='zip') >>> >>> resource_fields = {} >>> resource_fields['name'] = fields.String >>> resource_fields['billing_address'] = fields.Nested(address_fields) >>> resource_fields['shipping_address'] = fields.Nested(address_fields) >>> address1 = {'addr1': '123 fake street', 'city': 'New York', 'state': 'NY', 'zip': '10468'} >>> address2 = {'addr1': '555 nowhere', 'city': 'New York', 'state': 'NY', 'zip': '10468'} >>> data = { 'name': 'bob', 'billing_address': address1, 'shipping_address': address2} >>> >>> json.dumps(marshal_with(data, resource_fields)) '{"billing_address": {"line 1": "123 fake street", "line 2": null, "state": "NY", "zip": "10468", "city": "New York"}, "name": "bob", "shipping_address": {"line 1": "555 nowhere", "line 2": null, "state": "NY", "zip": "10468", "city": "New York"}}'
此示例使用兩個嵌套字段。Nested
構造函數把字段的字典作爲子字段(sub-fields)來呈現。使用 Nested
和之前例子中的嵌套字典之間的重要區別就是屬性的上下文。在本例中 “billing_address” 是一個具有自己字段的複雜的對象,傳遞給嵌套字段的上下文是子對象(sub-object),而不是原來的“數據”對象。換句話說,data.billing_address.addr1
是在這裏的範圍(譯者:這裏是直譯),然而在之前例子中的 data.addr1
是位置屬性。記住:嵌套和列表對象創建一個新屬性的範圍。
如下,我們這裏使用嵌套字段實現上面那個案例
user_fields = { 'id': fields.Integer(attribute='id'), 'private_name': fields.String(attribute='username',default='匿名'), 'password': fields.String, 'isDelete': fields.Boolean(attribute='isdelete'), 'isDelete1': IsDelete(attribute='isdelete'), 'udatetime': fields.DateTime(dt_format='rfc822') } user_friend_fields = { 'username': fields.String, 'nums': fields.Integer, 'friends': fields.List(fields.Nested(user_fields)) } class UserFriendResource(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': friend_list # [user,user,user] } return data
如下,我們不用marshal了,用marshal_with。我們的輸出數據還是data,data裏面有friend_list這個model對象列表。把data返回去,那麼需要將它交給marsh_with這個裝飾器處理,這個裝飾器裏面需要指定user_friend_fields這個我們定義的輸出字段格式,這個輸出字段格式和我們在get方法裏面定義的數據結構是一樣的,但是有model對象的地方它是做了處理的。處理的方式就是使用fields.List(),將朋友對象列表,解組成朋友信息列表,然後指定user_fields這個輸出字段格式來輸出給客戶端。而user_fields是之前定義的用戶詳情輸出字段格式。
如果返回的數據中有多個model對象或者對象列表,那麼應該也是可以這樣做的,然後在外面定義一個同樣數據結構的字段格式。將這個字段格式有model對象或對象列表的地方再使用下面的方式來嵌套其它輸出字段格式。這就完成了複雜的數據結構輸出。如果friends_list裏面還要更復雜的嵌套那就一層層往裏面嵌套,有時間去試試
這一章總結
知識點
什麼是RESTful架構: (1)每一個URI代表一種資源; (2)客戶端和服務器之間,傳遞這種資源的某種表現層; (3)客戶端通過四個HTTP動詞(GET,POST,PUT,DELETE,[PATCH]),對服務器端資源進行操作,實現"表現層狀態轉化"。 Postman 前後端分離: 前端: app,小程序,pc頁面 後端: 沒有頁面,mtv: 模型模板視圖 去掉了t模板。 mv:模型 視圖 模型的使用:跟原來的用法相同 視圖: api構建視圖 步驟: 1. pip3 install flask-restful 2.創建api對象 api = Api(app=app) api = Api(app=藍圖對象) 3. 定義類視圖: from flask_restful import Resource class xxxApi(Resource): def get(self): pass def post(self): pass def put(self): pass def delete(self): pass 4. 綁定 api.add_resource(xxxApi,'/user') 參照:http://www.pythondoc.com/Flask-RESTful/quickstart.html https://flask-restful.readthedocs.io/en/latest/ 路由: @app.route('/user') def user(): -------》視圖函數 ..... return response對象 增加 修改 刪除 查詢 按鈕動作 http://127.0.0.1:5000/user?id=1 http://127.0.0.1:5000/user/1 restful: ---->api ----> 接口 ---->資源 ----> url class xxx(Resource): -------> 類視圖 def get(self): pass .... http://127.0.0.1:5000/user get post put delete 增加 修改 刪除 查詢 是通過請求方式完成的 路徑產生: api.add_resource(Resource的子類,'/user') api.add_resource(Resource的子類,'/goods') api.add_resource(Resource的子類,'/order') endpoint: http://127.0.0.1:5000/user/1 http://127.0.0.1:5000/goods?type=xxx&page=1&sorted=price ----》get ----------------進:請求參數傳入------------------- 步驟: 1。創建RequestParser對象: # 參數解析 parser = reqparse.RequestParser(bundle_errors=True) # 解析對象 2。給解析器添加參數: 通過parser.add_argument('名字',type=類型,required=是否必須填寫,help=錯誤的提示信息,location=表明獲取的位置form就是post表單提交) 注意在type的位置可以添加一些正則的驗證等。 例如: 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='必須輸入6~12位數字密碼', location=['form']) parser.add_argument('phone', type=inputs.regex(r'^1[356789]\d{9}$'), location=['form'], help='手機號碼格式錯誤') parser.add_argument('hobby', action='append') # ['籃球', '遊戲', '旅遊'] parser.add_argument('icon', type=FileStorage, location=['files']) 只要添加上面的內容,就可以控制客戶端的提交,以及提交的格式。 3。在請求的函數中獲取數據: 可以在get,post,put等中獲取數據,通過parser對象.parse_args() # 獲取數據 args = parser.parse_args() args是一個字典底層的結構中,因此我們獲取具體的數據時可以通過get username = args.get('username') password = args.get('password') ------------輸出----------------- 1.需要定義字典,字典的格式就是給客戶端看的格式 user_fields = { 'id': fields.Integer, 'username': fields.String(default='匿名'), 'pwd': fields.String(attribute='password'), 'udatetime': fields.DateTime(dt_format='rfc822') } 客戶端能看到的是: id,username,pwd,udatetime這四個key 默認key的名字是跟model中的模型屬性名一致,如果不想讓前端看到命名,則可以修改 但是必須結合attribute='模型的字段名' 自定義fields 1。必須繼承Raw 2。重寫方法: def format(self): return 結果 class IsDelete(fields.Raw): def format(self, value): print('------------------>', value) return '刪除' if value else '未刪除' user_fields = { 。。。 'isDelete1': IsDelete(attribute='isdelete'), 。。。 } URI: xxxlist ----->點擊具體的一個獲取詳情 ------> 詳情 定義兩個user_fields, 1.用於獲取用戶的列表信息結構的fields: user_fields_1 = { 'id': fields.Integer, 'username': fields.String(default='匿名'), 'uri': fields.Url('single_user', absolute=True) ----》參數使用的就是endpoint的值 } 2。具體用戶信息展示的fields user_fields = { 'id': fields.Integer, 'username': fields.String(default='匿名'), 'pwd': fields.String(attribute='password'), 'isDelete': fields.Boolean(attribute='isdelete'), 'isDelete1': IsDelete(attribute='isdelete'), 'udatetime': fields.DateTime(dt_format='rfc822') } 涉及endpoint的定義: api.add_resource(UserSimpleResource, '/user/<int:id>', endpoint='single_user') 出: return data 注意:data必須是符合json格式 { 'aa':10, 'bb':[ { 'id':1, 'xxxs':[ {},{} ] }, { } ] } 如果直接返回不能有自定義的對象User,Friend,。。。。 如果有這種對象,需要:marchal(),marchal_with()幫助進行轉換。 1。marchal(對象,對象的fields格式) # 對象的fields格式是指字典的輸出格式 marchal([對象,對象],對象的fields格式) 2。marchal_with() 作爲裝飾器修飾請求方法 @marshal_with(user_friend_fields) def get(self, id): 。。。。 return data 函數需要參數,參數就是最終數據輸出的格式 參數: user_friend_fields,類型是:dict類型 例如: user_friend_fields = { 'username': fields.String, 'nums': fields.Integer, 'friends': fields.List(fields.Nested(user_fields)) } fields.Nested(fields.String) ----> ['aaa','bbb','bbbc'] fields.Nested(user_fields) -----> user_fields是一個字典結構,將裏面的每一個對象轉成user_fields -----》[user,user,user]
程序
import os from flask import Blueprint, url_for from flask_restful import Resource, marshal_with, fields, reqparse, inputs, marshal from werkzeug.datastructures import FileStorage from apps.user.model import User, Friend from exts import api, db from settings import Config user_bp = Blueprint('user', __name__, url_prefix='/api') class IsDelete(fields.Raw): def format(self, value): # print('------------------>', value) return '刪除' if value else '未刪除' user_fields_1 = { 'id': fields.Integer, 'username': fields.String(default='匿名'), 'uri': fields.Url('single_user', absolute=True) } user_fields = { 'id': fields.Integer, 'username': fields.String(default='匿名'), 'pwd': fields.String(attribute='password'), 'isDelete': fields.Boolean(attribute='isdelete'), 'isDelete1': IsDelete(attribute='isdelete'), 'udatetime': fields.DateTime(dt_format='rfc822') } # 參數解析 parser = reqparse.RequestParser(bundle_errors=True) # 解析對象 # a783789893hf request.form.get() | request.args.get() | request.cookies.get() | request.headers.get() 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='必須輸入6~12位數字密碼', location=['form']) parser.add_argument('phone', type=inputs.regex(r'^1[356789]\d{9}$'), location=['form'], help='手機號碼格式錯誤') parser.add_argument('hobby', action='append') # ['籃球', '遊戲', '旅遊'] parser.add_argument('icon', type=FileStorage, location=['files']) # 定義類視圖 class UserResource(Resource): # get 請求的處理 @marshal_with(user_fields_1) def get(self): users = User.query.all() # userList = [] # for user in users: # userList.append(user.__dict__) return users # post @marshal_with(user_fields) def post(self): # 獲取數據 args = parser.parse_args() username = args.get('username') password = args.get('password') phone = args.get('phone') bobby = args.get('hobby') print(bobby) icon = args.get('icon') print(icon) # 創建user對象 user = User() user.username = username user.password = password if icon: upload_path = os.path.join(Config.UPLOAD_ICON_DIR, icon.filename) icon.save(upload_path) # 保存路徑個 user.icon = os.path.join('upload/icon', icon.filename) if phone: user.phone = phone db.session.add(user) db.session.commit() return user # put def put(self): return {'msg': '------>put'} # delete def delete(self): return {'msg': '------>delete'} class UserSimpleResource(Resource): @marshal_with(user_fields) # user轉成一個序列化對象, def get(self, id): user = User.query.get(id) return user # 不是str,list,int,。。。 def put(self, id): print('endpoint的使用:', url_for('all_user')) return {'msg': 'ok'} def delete(self, id): pass user_friend_fields = { 'username': fields.String, 'nums': fields.Integer, 'friends': fields.List(fields.Nested(user_fields)) } class UserFriendResource(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': friend_list # [user,user,user] } return data api.add_resource(UserResource, '/user', endpoint='all_user') api.add_resource(UserSimpleResource, '/user/<int:id>', endpoint='single_user') api.add_resource(UserFriendResource, '/friend/<int:id>', endpoint='user_friend')
import datetime from exts import db class Friend(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) uid = db.Column(db.Integer, db.ForeignKey('user.id')) fid = db.Column(db.Integer, db.ForeignKey('user.id')) class User(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) username = db.Column(db.String(15), nullable=False) password = db.Column(db.String(12), nullable=False) phone = db.Column(db.String(11)) icon = db.Column(db.String(150)) isdelete = db.Column(db.Boolean()) email = db.Column(db.String(100)) udatetime = db.Column(db.DateTime, default=datetime.datetime.now) friends = db.relationship('Friend', backref='user', foreign_keys=Friend.uid) def __str__(self): return self.username
藍圖中寫restful api 待添加