詳解Flask前后端分離項(xiàng)目案例
簡(jiǎn)介
學(xué)習(xí)慕課課程,F(xiàn)lask前后端分離API后臺(tái)接口的實(shí)現(xiàn)demo,前端可以接入小程序,暫時(shí)已經(jīng)完成后臺(tái)API基礎(chǔ)架構(gòu),使用 postman 調(diào)試.git
重構(gòu)部分:
- ken校驗(yàn)?zāi)K
- auths認(rèn)證模塊
- scope權(quán)限模塊,增加全局掃描器(參考flask HTTPExceptions模塊)
收獲
- 我們可以接受定義時(shí)的復(fù)雜,但不能接受調(diào)用時(shí)的復(fù)雜
- 如果你覺得寫代碼厭倦,無聊,那你只是停留在功能的實(shí)現(xiàn)上,功能的實(shí)現(xiàn)很簡(jiǎn)單,你要追求的是更好的寫法,抽象的藝術(shù),不是機(jī)械的勞動(dòng)而是要 創(chuàng)造 ,要有自己的思考
- Sqlalchemy 中對(duì)類的創(chuàng)建都是用元類的方式,所以調(diào)用的時(shí)候都不用實(shí)例化,當(dāng)我們重寫 __init__ 方法是需要調(diào)用 orm.reconstrcut 裝飾器,才會(huì)執(zhí)行實(shí)例化對(duì)象的構(gòu)造函數(shù)
- 權(quán)限等級(jí)模塊的設(shè)計(jì)( api訪問權(quán)限 ),如超級(jí)管理員,管理員,普通用戶,訪客,這四者之間的關(guān)系,有包含的關(guān)系,所以可以考慮合并也可以考慮排除的方式來構(gòu)建權(quán)限控制模塊. 參考本項(xiàng)目中的 app.libs.scope
- 學(xué)的是解決問題的方法,首先要有深度,在去考慮廣度,還要懂得遷移應(yīng)用,形成自己的思維模型。
知識(shí)點(diǎn)復(fù)盤
初始化flask應(yīng)用程序
app = Flask(__name__, static_folder='views/statics', static_url_path='/static', template_folder="templates")
創(chuàng)建Flask應(yīng)用程序?qū)嵗龑?duì)象, 如果模塊存在,會(huì)根據(jù)模塊所在的目錄去尋找靜態(tài)文件和模塊文件, 如果模塊不存在,會(huì)默認(rèn)使用app對(duì)象所在的項(xiàng)目目錄
- __name__ 表示以此模塊所在的目錄作為工作目錄,就是靜態(tài)文等從這個(gè)目錄下去找
- static_folder 指定靜態(tài)文件存放相對(duì)路徑 flask默認(rèn)會(huì)用/進(jìn)行分割然后取最后一個(gè)作為訪問 url 類似 Django 中的 STATICFILES_DIRS
- static_url_path 指定訪問靜態(tài)文件的 url 地址前綴, 類似 Django 中的 STATIC_URL
- template_folder 指定模板文件的目錄
@property
def static_url_path(self):
"""The URL prefix that the static route will be accessible from.
If it was not configured during init, it is derived from
:attr:`static_folder`.
"""
if self._static_url_path is not None:
return self._static_url_path
if self.static_folder is not None:
basename = os.path.basename(self.static_folder)
return ("/" + basename).rstrip("/")
@static_url_path.setter
def static_url_path(self, value):
if value is not None:
value = value.rstrip("/")
self._static_url_path = value
Flask 中 url 相關(guān)底層類
- BaseConverter 子類:保存提取 url 參數(shù)匹配規(guī)則
- Rule 類:記錄一個(gè) url 和一個(gè)視圖函數(shù)的對(duì)應(yīng)關(guān)系
- Map 類:記錄所有 url 地址和試圖函數(shù)對(duì)應(yīng)的關(guān)系 Map(Rule, Rule, ....)
- MapAdapter 類:執(zhí)行 url 匹配的過程,其中有一個(gè) match 方法, Rule.match(path, method)
自定義路由管理器
from flask import Flask
app = Flask(__name__)
from werkzeug.routing import BaseConverter
class RegexUrl(BaseConverter):
# 指定匹配參數(shù)時(shí)的正則表達(dá)式
# 如: # regex = '\d{6}'
def __init__(self, url_map, regex):
"""
:param url_map: flask會(huì)自動(dòng)傳遞該參數(shù)
:param regex: 自定義的匹配規(guī)則
"""
super(RegexUrl, self).__init__(url_map)
self.regex = regex
# 在對(duì)應(yīng)的試圖函數(shù)之前調(diào)用
# 從url中提取出參數(shù)之后,會(huì)先調(diào)用to_python
# 會(huì)把提取出的值作為參數(shù)傳遞給to_pthon在返回給對(duì)應(yīng)的試圖
def to_python(self, value):
"""可以在這里做一些參數(shù)的類型轉(zhuǎn)換"""
return value
# 調(diào)用url_for時(shí)會(huì)被調(diào)用, 用來處理url反向解析時(shí)url參數(shù)處理
# 返回值用來拼接url
def to_url(self, value):
"""對(duì)接收到參數(shù)做一些過濾等"""
return value
# 將自定義路由轉(zhuǎn)換器類添加到轉(zhuǎn)換器字典中
app.url_map.converters['re'] = RegexUrl
# 案例
@app.route('/user/<re("[a-z]{3}"):id>')
def hello(id):
return f'hello {id}'
if __name__ == '__main__':
app.run(debug=True)
全局異常捕獲
AOP編程思想,面向切面編程,把事件統(tǒng)一在一個(gè)地方處理,在一個(gè)統(tǒng)一的出口做處理
errorhandler 在flask 1.0版本之前只支持填寫對(duì)應(yīng)的錯(cuò)誤碼,比如 @app.errorhandler(404)
在flask1.0版本之后就支持全局的異常捕獲了 @app.errorhandler(code_or_exception) ,有了這個(gè)之后,就可以在全局做一個(gè)異常捕獲了,不用每個(gè)視圖函數(shù)都做異常捕獲。
@app.errorhandler(Exception)
def framework_error(e):
if isinstance(e, APIException):
return e
elif isinstance(e, HTTPException):
code = e.code
msg = e.description
error_code = 1007
return APIException(msg, code, error_code)
else:
if not current_app.config['DEBUG']:
return ServerError()
else:
raise e
異常類型
- 可預(yù)知的異常(已知異常)
- 完全沒有意識(shí)的異常(未知異常)
- abort函數(shù)
- abort(狀態(tài)碼) 是一個(gè)默認(rèn)的拋出異常的方法
- 調(diào)用abort函數(shù)可以拋出一個(gè)指定狀態(tài)碼對(duì)應(yīng)的異常信息
- abort函數(shù)會(huì)立即終止當(dāng)前視圖函數(shù)的運(yùn)行**
模型對(duì)象的序列化
場(chǎng)景:我們有時(shí)候可能需要返回模型對(duì)象中的某些字段,或者全部字段,平時(shí)的做法就是將對(duì)象中的各個(gè)字段轉(zhuǎn)為字典在返回 jsonnify(data) , 但是這樣的寫法可能在每個(gè)需要返回?cái)?shù)據(jù)的試圖函數(shù)中都寫一個(gè)對(duì)應(yīng)的字典。。對(duì)象轉(zhuǎn)字典在返回。 json 默認(rèn)是不能序列化對(duì)象的,一般我們的做法是 json.dumps(obj, default=lambda o: o.__dict__) 但是 __dict__ 中只保存實(shí)例屬性,我們的模型類基本定義的類屬性。解決這個(gè)問題就要看 jsonify 中是如何做序列化的,然后怎么重寫。
重寫 JSONEncoder
from datetime import date
from flask import Flask as _Flask
from flask.json import JSONEncoder as _JSONEncoder
class JSONEncoder(_JSONEncoder):
"""
重寫json序列化,使得模型類的可序列化
"""
def default(self, o):
if hasattr(o, 'keys') and hasattr(o, '__getitem__'):
return dict(o)
if isinstance(o, date):
return o.strftime('%Y-%m-%d')
super(JSONEncoder, self).default(o)
# 需要將重寫的類綁定到應(yīng)用程序中
class Flask(_Flask):
json_encoder = JSONEncoder
模型類的定義
class User(Base):
id = Column(Integer, primary_key=True)
email = Column(String(24), unique=True, nullable=False)
nickname = Column(String(24), unique=True)
auth = Column(SmallInteger, default=1)
_password = Column('password', String(100))
def keys(self):
return ['id', 'email', 'nickname', 'auth']
def __getitem__(self, item):
return getattr(self, item)
注意: 修改了 json_encode 方法后,只要調(diào)用到 flask.json 模塊的都會(huì)走這個(gè)方法
為什么要寫 keys 和 __getitem__ 方法
當(dāng)我們使用 dict(object) 操作一個(gè)對(duì)象的時(shí)候, dict 首先會(huì)到實(shí)例中找 keys 的方法,將其返回列表的值作為 key , 然后會(huì)根據(jù) object[key] 獲取對(duì)應(yīng)的值,所以實(shí)例要實(shí)現(xiàn) __getitem__ 方法才可以使用中括號(hào)的方式調(diào)用屬性
進(jìn)階寫法- 控制返回的字段
場(chǎng)景:當(dāng)我們有一個(gè) Book 的模型類,我們的 api 接口可能需要返回 book 的詳情頁(yè)所以就要返回所有字典,但另外一個(gè)接口可能只需要返回某幾個(gè)字段。
class Book(Base):
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50), nullable=False)
author = Column(String(30), default='未名')
binding = Column(String(20))
publisher = Column(String(50))
price = Column(String(20))
pages = Column(Integer)
pubdate = Column(String(20))
isbn = Column(String(15), nullable=False, unique=True)
summary = Column(String(1000))
image = Column(String(50))
# orm實(shí)例化對(duì)象, 字段需要寫在構(gòu)造函數(shù)中,這樣每個(gè)實(shí)例對(duì)象都會(huì)有自己的一份,刪除增加都不會(huì)互相影響
@orm.reconstructor
def __init__(self):
self.fields = ['id', 'title', 'author', 'binding',
'publisher', 'price', 'pages', 'pubdate',
'isbn', 'summary', 'image']
def keys(self):
return self.fields if hasattr(self, 'fields') else []
def hide(self, *keys):
for key in keys:
self.fields.remove(key)
return self
def append(self, *keys):
for key in keys:
self.fields.append(key)
return self
@api.route('/search')
def search():
books = Book.query.filter().all() # 根據(jù)某些條件搜索的
books = [book.hide('summary') for book in books]
return jsonify(books)
@api,route('/<isbn>/detail')
def detail(isbn):
book = Book.query.filter_by(isbn=isbn).first_or_404()
return jsonify(book)
請(qǐng)求鉤子函數(shù)
- before_first_request:在處理第一個(gè)請(qǐng)求前運(yùn)行。
- before_request:在每次請(qǐng)求前運(yùn)行。
- after_request:如果沒有未處理的異常拋出,在每次請(qǐng)求后運(yùn)行。
- teardown_request:在每次請(qǐng)求后運(yùn)行,即使有未處理的異常拋出。
全局掃描器
模仿flask exceptions 預(yù)加載各個(gè)異常類的方式,將用戶組自動(dòng)加載進(jìn)內(nèi)存中,這樣獲取的話就更方便
str2obj = {}
level2str = {}
def iteritems(d, *args, **kwargs):
return iter(d.items(*args, **kwargs))
def _find_scope_group():
for _name, obj in iteritems(globals()):
try:
is_scope_obj = issubclass(obj, BaseScope)
except TypeError:
is_scope_obj = False
if not is_scope_obj or obj.level < 1:
continue
old_obj = str2obj.get(_name, None)
if old_obj is not None and issubclass(obj, old_obj):
continue
str2obj[_name] = obj
level2str[obj.level] = _name
# 模仿flask exceptions 預(yù)加載各個(gè)異常類的方式,將用戶組自動(dòng)加載進(jìn)內(nèi)存
_find_scope_group()
del _find_scope_group
常見bug
form 正則校驗(yàn)注意事項(xiàng)
r'{6, 25}$'
帶空格和不帶空格是兩碼事, 正則里面{,} 連續(xù)不帶空格
r'{6,25}$'
參考
Python Flask高級(jí)編程之RESTFul API前后端分離精講
到此這篇關(guān)于詳解Flask前后端分離項(xiàng)目案例的文章就介紹到這了,更多相關(guān)Flask前后端分離 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談pytorch池化maxpool2D注意事項(xiàng)
今天小編就為大家分享一篇淺談pytorch池化maxpool2D注意事項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-02-02
pandas數(shù)據(jù)清洗實(shí)現(xiàn)刪除的項(xiàng)目實(shí)踐
本文主要介紹了pandas數(shù)據(jù)清洗實(shí)現(xiàn)刪除的項(xiàng)目實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
python進(jìn)行數(shù)據(jù)預(yù)處理的4個(gè)重要步驟
在數(shù)據(jù)科學(xué)項(xiàng)目中,數(shù)據(jù)預(yù)處理是最重要的事情之一,本文詳細(xì)給大家介紹python進(jìn)行數(shù)據(jù)預(yù)處理的4個(gè)重要步驟:拆分訓(xùn)練集和測(cè)試集,處理缺失值,處理分類特征和進(jìn)行標(biāo)準(zhǔn)化處理,需要的朋友可以參考下2023-06-06
使用Python開發(fā)游戲運(yùn)行腳本成功調(diào)用大漠插件
閑來無事,想通過python來實(shí)現(xiàn)一些簡(jiǎn)單的游戲輔助腳本,而游戲輔助腳本的主要原理就是通過程序來查找游戲程序窗口,模擬實(shí)現(xiàn)鼠標(biāo)點(diǎn)擊和鍵盤按鍵等事件來實(shí)現(xiàn)游戲輔助的,對(duì)Python開發(fā)游戲運(yùn)行腳本相關(guān)知識(shí)感興趣的朋友跟隨小編一起看看吧2021-11-11
?python中pandas讀取csv文件?時(shí)如何省去csv.reader()操作指定列步驟
這篇文章主要介紹了?python中pandas讀取csv文件?時(shí)如何省去csv.reader()操作指定列步驟,對(duì)正在工作的你可能有一定的幫助,需要的朋友可以參考一下2022-01-01

