學習慕課課程,Flask先後端分離API後臺接口的實現demo,前端能夠接入小程序,暫時已經完成後臺API基礎架構,使用postman
調試. githtml
重構部分:前端
flask HTTPExceptions
模塊)Sqlalchemy
中對類的建立都是用元類的方式,因此調用的時候都不用實例化,當咱們重寫__init__
方法是須要調用orm.reconstrcut
裝飾器,纔會執行實例化對象的構造函數api訪問權限
),如超級管理員,管理員,普通用戶,訪客,這四者之間的關係,有包含的關係,因此能夠考慮合併也能夠考慮排除的方式來構建權限控制模塊. 參考本項目中的app.libs.scope
推薦閱讀:
工做中如何作好技術積累
沒有技術深度的苦惱python
app = Flask(__name__, static_folder='views/statics', static_url_path='/static', template_folder="templates")
建立Flask應用程序實例對象, 若是模塊存在,會根據模塊所在的目錄去尋找靜態文件和模塊文件, 若是模塊不存在,會默認使用app對象所在的項目目錄git
__name__
表示以此模塊所在的目錄做爲工做目錄,就是靜態文等從這個目錄下去找static_folder
指定靜態文件存放相對路徑 flask默認會用/進行分割而後取最後一個做爲訪問url
相似Django
中的STATICFILES_DIRS
static_url_path
指定訪問靜態文件的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
url
相關底層類BaseConverter
子類:保存提取url
參數匹配規則Rule
類:記錄一個url
和一個視圖函數的對應關係Map
類:記錄全部url
地址和試圖函數對應的關係 Map(Rule, Rule, ....)
MapAdapter
類:執行url
匹配的過程,其中有一個match
方法,Rule.match(path, method)
from flask import Flask app = Flask(__name__) from werkzeug.routing import BaseConverter class RegexUrl(BaseConverter): # 指定匹配參數時的正則表達式 # 如: # regex = '\d{6}' def __init__(self, url_map, regex): """ :param url_map: flask會自動傳遞該參數 :param regex: 自定義的匹配規則 """ super(RegexUrl, self).__init__(url_map) self.regex = regex # 在對應的試圖函數以前調用 # 從url中提取出參數以後,會先調用to_python # 會把提取出的值做爲參數傳遞給to_pthon在返回給對應的試圖 def to_python(self, value): """能夠在這裏作一些參數的類型轉換""" return value # 調用url_for時會被調用, 用來處理url反向解析時url參數處理 # 返回值用來拼接url def to_url(self, value): """對接收到參數作一些過濾等""" return value # 將自定義路由轉換器類添加到轉換器字典中 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編程思想,面向切面編程,把事件統一在一個地方處理,在一個統一的出口作處理github
errorhandler
在flask 1.0版本以前只支持填寫對應的錯誤碼,好比 @app.errorhandler(404)
正則表達式
在flask1.0版本以後就支持全局的異常捕獲了@app.errorhandler(code_or_exception)
,有了這個以後,就能夠在全局作一個異常捕獲了,不用每一個視圖函數都作異常捕獲。編程
@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
場景:咱們有時候可能須要返回模型對象中的某些字段,或者所有字段,平時的作法就是將對象中的各個字段轉爲字典在返回jsonnify(data)
, 可是這樣的寫法可能在每一個須要返回數據的試圖函數中都寫一個對應的字典。。對象轉字典在返回。json
默認是不能序列化對象的,通常咱們的作法是 json.dumps(obj, default=lambda o: o.__dict__)
可是 __dict__
中只保存實例屬性,咱們的模型類基本定義的類屬性。解決這個問題就要看jsonify
中是如何作序列化的,而後怎麼重寫。json
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) # 須要將重寫的類綁定到應用程序中 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
方法後,只要調用到flask.json
模塊的都會走這個方法flask
爲何要寫keys
和__getitem__
方法小程序
當咱們使用
dict(object)
操做一個對象的時候,dict
首先會到實例中找keys
的方法,將其返回列表的值做爲key
, 而後會根據object[key]
獲取對應的值,因此實例要實現__getitem__
方法纔可使用中括號的方式調用屬性
進階寫法 - 控制返回的字段
場景:當咱們有一個Book
的模型類,咱們的api
接口可能須要返回book
的詳情頁因此就要返回全部字典,但另一個接口可能只須要返回某幾個字段。
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實例化對象, 字段須要寫在構造函數中,這樣每一個實例對象都會有本身的一份,刪除增長都不會互相影響 @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() # 根據某些條件搜索的 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)
模仿flask exceptions 預加載各個異常類的方式,將用戶組自動加載進內存中,這樣獲取的話就更方便
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 預加載各個異常類的方式,將用戶組自動加載進內存 _find_scope_group() del _find_scope_group
form
正則校驗注意事項r'[1]{6, 25}$'
帶空格和不帶空格是兩碼事, 正則裏面{,} 連續不帶空格
r'[2]{6,25}$'
Python Flask高級編程之RESTFul API先後端分離精講
七月老師的課程挺好的,不是純寫代碼,而是從問題入手,怎麼把複雜問題簡單化,從0到1。