欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Flask前后端分離項(xiàng)目案例

 更新時(shí)間:2020年07月24日 08:29:18   作者:返回主頁JonPan  
這篇文章主要介紹了Flask前后端分離項(xiàng)目案例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

簡介

學(xué)習(xí)慕課課程,F(xiàn)lask前后端分離API后臺接口的實(shí)現(xiàn)demo,前端可以接入小程序,暫時(shí)已經(jīng)完成后臺API基礎(chǔ)架構(gòu),使用 postman 調(diào)試.git

重構(gòu)部分:

  • ken校驗(yàn)?zāi)K
  • auths認(rèn)證模塊
  • scope權(quán)限模塊,增加全局掃描器(參考flask HTTPExceptions模塊)

收獲

  1. 我們可以接受定義時(shí)的復(fù)雜,但不能接受調(diào)用時(shí)的復(fù)雜
  2. 如果你覺得寫代碼厭倦,無聊,那你只是停留在功能的實(shí)現(xiàn)上,功能的實(shí)現(xiàn)很簡單,你要追求的是更好的寫法,抽象的藝術(shù),不是機(jī)械的勞動而是要 創(chuàng)造 ,要有自己的思考
  3. Sqlalchemy 中對類的創(chuàng)建都是用元類的方式,所以調(diào)用的時(shí)候都不用實(shí)例化,當(dāng)我們重寫 __init__ 方法是需要調(diào)用 orm.reconstrcut 裝飾器,才會執(zhí)行實(shí)例化對象的構(gòu)造函數(shù)
  4. 權(quán)限等級模塊的設(shè)計(jì)( api訪問權(quán)限 ),如超級管理員,管理員,普通用戶,訪客,這四者之間的關(guān)系,有包含的關(guān)系,所以可以考慮合并也可以考慮排除的方式來構(gòu)建權(quán)限控制模塊. 參考本項(xiàng)目中的 app.libs.scope
  5. 學(xué)的是解決問題的方法,首先要有深度,在去考慮廣度,還要懂得遷移應(yīng)用,形成自己的思維模型。

知識點(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ū)嵗龑ο? 如果模塊存在,會根據(jù)模塊所在的目錄去尋找靜態(tài)文件和模塊文件, 如果模塊不存在,會默認(rèn)使用app對象所在的項(xiàng)目目錄

  • __name__ 表示以此模塊所在的目錄作為工作目錄,就是靜態(tài)文等從這個(gè)目錄下去找
  • static_folder 指定靜態(tài)文件存放相對路徑 flask默認(rèn)會用/進(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ù)的對應(yīng)關(guān)系
  • Map 類:記錄所有 url 地址和試圖函數(shù)對應(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會自動傳遞該參數(shù)
    :param regex: 自定義的匹配規(guī)則
    """
    super(RegexUrl, self).__init__(url_map)
    self.regex = regex
  
  # 在對應(yīng)的試圖函數(shù)之前調(diào)用
  # 從url中提取出參數(shù)之后,會先調(diào)用to_python
  # 會把提取出的值作為參數(shù)傳遞給to_pthon在返回給對應(yīng)的試圖
  def to_python(self, value):
    """可以在這里做一些參數(shù)的類型轉(zhuǎn)換"""
    return value
  
  # 調(diào)用url_for時(shí)會被調(diào)用, 用來處理url反向解析時(shí)url參數(shù)處理
 # 返回值用來拼接url
  def to_url(self, value):
    """對接收到參數(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版本之前只支持填寫對應(yīng)的錯誤碼,比如 @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ù)知的異常(已知異常)
  • 完全沒有意識的異常(未知異常)
  • abort函數(shù)
  • abort(狀態(tài)碼) 是一個(gè)默認(rèn)的拋出異常的方法
  • 調(diào)用abort函數(shù)可以拋出一個(gè)指定狀態(tài)碼對應(yīng)的異常信息
  • abort函數(shù)會立即終止當(dāng)前視圖函數(shù)的運(yùn)行**

模型對象的序列化

場景:我們有時(shí)候可能需要返回模型對象中的某些字段,或者全部字段,平時(shí)的做法就是將對象中的各個(gè)字段轉(zhuǎn)為字典在返回 jsonnify(data) , 但是這樣的寫法可能在每個(gè)需要返回?cái)?shù)據(jù)的試圖函數(shù)中都寫一個(gè)對應(yīng)的字典。。對象轉(zhuǎn)字典在返回。 json 默認(rèn)是不能序列化對象的,一般我們的做法是 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 模塊的都會走這個(gè)方法

為什么要寫 keys 和 __getitem__ 方法

當(dāng)我們使用 dict(object) 操作一個(gè)對象的時(shí)候, dict 首先會到實(shí)例中找 keys 的方法,將其返回列表的值作為 key , 然后會根據(jù) object[key] 獲取對應(yīng)的值,所以實(shí)例要實(shí)現(xiàn) __getitem__ 方法才可以使用中括號的方式調(diào)用屬性

進(jìn)階寫法- 控制返回的字段

場景:當(dāng)我們有一個(gè) Book 的模型類,我們的 api 接口可能需要返回 book 的詳情頁所以就要返回所有字典,但另外一個(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í)例化對象, 字段需要寫在構(gòu)造函數(shù)中,這樣每個(gè)實(shí)例對象都會有自己的一份,刪除增加都不會互相影響
  @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)

請求鉤子函數(shù)

  • before_first_request:在處理第一個(gè)請求前運(yùn)行。
  • before_request:在每次請求前運(yùn)行。
  • after_request:如果沒有未處理的異常拋出,在每次請求后運(yùn)行。
  • teardown_request:在每次請求后運(yùn)行,即使有未處理的異常拋出。

全局掃描器

模仿flask exceptions 預(yù)加載各個(gè)異常類的方式,將用戶組自動加載進(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è)異常類的方式,將用戶組自動加載進(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高級編程之RESTFul API前后端分離精講

到此這篇關(guān)于詳解Flask前后端分離項(xiàng)目案例的文章就介紹到這了,更多相關(guān)Flask前后端分離 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論