1. 程式人生 > 實用技巧 >【11】Flask 高階進階

【11】Flask 高階進階

01 請求擴充套件

01 before_first_request :專案啟動後第一次請求的時候執行

@app.before_first_request
def before_first_request():
    print('第一次請求的時候執行')

02 before_request:每次請求之前執行

@app.before_request
def before_request():
    print('每次請求之前執行')
    # return '直接return'    # 如果有一個寫了return返回值,那麼其他的before_request不會執行,檢視也不會執行。

注意:

  • 可以寫多個
  • 如果有一個寫了return返回值,那麼其他的before_request不會執行,檢視也不會執行。

03 after_request:每次請求之後執行,請求出現異常不會執行

def after_request(result):
    print('每次請求之後執行,請求出現異常不會執行')
    # 這個result是封裝的響應物件,需要return否則報錯
    return result

04 errorhandler:可以自定義監聽響應的狀態碼並處理:

@app.errorhandler(404)
def errorhandler(error):
    print(error)  # 是具體的錯誤資訊
    return '404頁面跑到了火星上面去了'

@app.errorhandler(500)
def errorhandler(error):
    print('errorhandler的錯誤資訊')
    print(error)
    return '伺服器內部錯誤500'

05 teardown_request:每次請求之後綁定了一個函式,在非debug模式下即使遇到了異常也會執行。

@app.teardown_request
def terardown_reqquest(error):
    print('無論檢視函式是否有錯誤,檢視函式執行完都會執行')
    print('想要此函式生效,debug不能為True')
    print('error 是具體的錯誤資訊')
    print(error)

06 template_global():全域性模板標籤

@app.template_global()
def add(a1, a2):
    return a1+a2
#{{add(1,2)}}

這個可以在模板中作為全域性的標籤使用,在模板中可以直接呼叫,呼叫方式:

{{add(1,2)}}

07 template_filter:全域性模板過濾器

@app.template_filter()
def add_filter(a1, a2, a3, a4):
    return a1 + a2 + a3 + a4

這個可以在模板中作為全域性過濾器使用,在模板中可以直接呼叫,呼叫方式(注意同template_global的區別) :

{{1|add_filter(2,3,4)}}

優勢:

全域性模板標籤和全域性模板過濾器簡化了需要手動傳一個函式給模板呼叫:

# app.py
```

def test(a1,a2):
    return a1+a2
@app.route('/')
def index():
    return render_template('index.html',test=test)

```

# index.html
```
{{test(22,22)}}

```

02 flask中介軟體

Flask的中介軟體的性質,就是可以理解為在整個請求的過程的前後定製一些個性化的功能。

flask的中介軟體的實現案例:
from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    print('檢視函式中')
    return 'hello world'

class my_middle:

    def __init__(self,wsgi_app):
        self.wsgi_app = wsgi_app

    def __call__(self, *args, **kwargs):
        print('中介軟體的程式碼上')
        obj = self.wsgi_app( *args, **kwargs)
        print('中介軟體的程式碼下')

        return obj

if __name__ == '__main__':
   
    app.wsgi_app = my_middle(app.wsgi_app)
     # app.wsgi_app(environ, start_response)
    app.run()
    # 梳理一下 根據werkzeug我們可以知道 每次請求必然經歷了app()
    # 所以要檢視Flask的原始碼找到__call__方法
    # 找到了__call__方法後發現執行了return self.wsgi_app(environ, start_response)
    # 然後flask裡面所有的內容排程都是基於這個self.wsgi_app(environ, start_response),這就是就是flask的入口
    # 如何實現中介軟體呢? 原理上就是重寫app.wsgi_app,然後在裡面新增上一些自己想要實現的功能。
    # 首先分析  app.wsgi_app需要加括號執行  所以我們把app.wsgi_app做成一個物件,並且這個物件需要加括號執行
    # 也就是會觸發這個物件的類的__call__()方法
    # 1 那麼就是app.wsgi_app=物件=自己重寫的類(app.wsgi_app) ,需要在自己重寫的類裡面實現flask原始碼中的app.wsgi_app,在例項化的過程把原來的app.wsgi_app變成物件的屬性
    # 2         app.wsgi_app() =物件() = 自己重寫的類.call()方法
    # 3         那麼上面的程式碼就可以理解了,在自己重寫的類中實現了原有的__call__方法
梳理:
  • 根據werkzeug我們可以知道 每次請求必然經歷了app()
  • 所以我們要檢視Flask的原始碼找到__call__方法
  • 找到了Flask的__call__方法後發現執行了return self.wsgi_app(environ, start_response)
  • flask裡面所有的內容排程都是基於這個self.wsgi_app(environ, start_response),這就是就是flask的入口,也就是selef是app,也就是app.wsgi_app(environ, start_response)為程式的入口。
  • 如何實現中介軟體呢? 原理上就是重寫app.wsgi_app,然後在裡面新增上一些自己想要實現的功能。
  • 首先分析 app.wsgi_app需要加括號執行 所以我們把app.wsgi_app做成一個物件,並且這個物件需要加括號執行。
  • 也就是會觸發這個物件的類的__call__()方法。
實操理解:
  1. app.wsgi_app=物件=自己重寫的類(app.wsgi_app)

    提示:我們需要在自己重寫的類裡面實現flask原始碼中的app.wsgi_app,在例項化的過程把原來的 app.wsgi_app變成物件的屬性

  2. app.wsgi_app() =物件() = 自己重寫的類.call()方法

app.wsgi_app(實參) =物件(實參) = 自己重寫的類.call(實參)方法

  1. 那麼上面的程式碼就可以理解了,在自己重寫的類中實現了原有的call方法,並且重新呼叫了原有的app.wsgi_app

03 藍圖:

3.1 藍圖的基本使用

在flask中,可以利用藍圖對程式目錄的劃分。

思考如果有很多個檢視函式,比如下面這樣是不是應該抽取出來專門的py檔案進行管理呢?

from flask import Flask

app = Flask(__name__)

@app.route('/login/')
def login():
    return "login"

@app.route('/logout/')
def logout():
    return "logout"

@app.route('/add_order/')
def add_order():
    return "add_order"

@app.route('/modify_order/')
def modify_order():
    return "modify_order"


if __name__ == '__main__':
    app.run()

上面的這種是不是會顯得主執行檔案特別亂,這個時候藍圖就閃亮登場了。

3.1.1例項:

專案目錄:

-templates
-static
-views
    -user.py
    -order.py
-app.py

views/user.py

from flask import Blueprint

# 1 建立藍圖
user_bp = Blueprint('user',__name__)

# 2 利用藍圖建立路由關係
@user_bp.route('/login/')
def login():
    return "login"

@user_bp.route('/logout/')
def logout():
    return "logout"

views/order.py

from flask import Blueprint

order_bp = Blueprint('order',__name__)

@order_bp.route('/add_order/')
def add_order():
    return "add_order"

@order_bp.route('/modify_order/')
def modify_order():
    return "modify_order"

app.py

from flask import Flask
from views.user import user_bp
from views.order import order_bp

app = Flask(__name__)
# 3 註冊藍圖
app.register_blueprint(user_bp)
app.register_blueprint(order_bp)


if __name__ == '__main__':
    app.run()

訪問:

其他的幾條路由也是直接訪問,在此就不做展示了。

講解:

觀察views/user.py

  • 可以把所有的檢視函式抽出來多個檔案。

  • 在這裡我們通過user_bp = Blueprint('user',__name__)建立一個藍圖物件

    引數講解:

    • user_bp :是用於指向創建出的藍圖物件,可以自由命名。
    • Blueprint的第一個引數自定義命名的‘user’用於url_for翻轉url時使用。
    • __name__用於尋找藍圖自定義的模板和靜態檔案使用。
  • 藍圖物件的用法和之前例項化出來的app物件用法很像,可以進行註冊路由。

觀察app.py

  • 這裡需要手動的去註冊一下藍圖,才會建立上url和檢視函式的對映關係。

關鍵詞:

  1. 建立藍圖

    user_bp = Blueprint('user',__name__)

  2. 利用藍圖建立路由關係

    @user_bp.route('/login/')
    def login():
    return "login"

  3. 註冊藍圖
    app.register_blueprint(bp)

3.2 藍圖的高階使用(重點)

3.2.1 藍圖中實現path部分的url字首

建立藍圖的時候填寫url_prefix可以為增加url的path部分的字首,可以更方便的去管理訪問檢視函式。

from flask import Blueprint

# 1 建立藍圖
user_bp = Blueprint('user',__name__,url_prefix='/user')
# 注意斜槓跟檢視函式的url連起來時候不要重複了。

注意:

  1. 斜槓跟檢視函式的url連起來時候不要重複了。

    圖解:

2.url加字首的時候也可以再註冊藍圖的時候加上,更推薦這麼做,因為程式碼的可讀性更強。

app.register_blueprint(user_bp,url_prefix='/order')

3.3.2 藍圖中自定義模板路徑

建立藍圖的時候填寫template_folder可以指定自定義模板路徑

# 1 建立藍圖                                           
# 所對應的引數路徑是相對於藍圖檔案的
user_bp = Blueprint('user',__name__,url_prefix='/user',template_folder='views_templates')

注意

  1. 藍圖雖然指定了自定義的模板查詢路徑,但是查詢順序還是會先找主app規定的模板路徑(templates),找不到再找藍圖自定義的模板路徑。

  2. Blueprinttemplate_folder引數指定的自定義模板路徑是相對於藍圖檔案的路徑。

    圖解:

    (01)

(02)

3.3.3 藍圖中自定義靜態檔案路徑

建立藍圖的時候填寫static_folder可以指定自定義靜態檔案的路徑

user_bp = Blueprint('user',__name__,url_prefix='/user',template_folder='views_templates',
                    static_folder='views_static')

注意:

  1. 在模板中使用自定義的靜態檔案路徑需要依賴url_for()
  2. 下節講解如何在模板中應用藍圖自定義的靜態檔案。

3.3.4 url_for()翻轉藍圖

檢視中翻轉url:

{{ url_for('建立藍圖時第一個引數.藍圖下的函式名') }}
# 如:
{{ url_for('user.login') }}

模板中翻轉url:

{{ url_for('建立藍圖時第一個引數.藍圖下的函式名') }}
# 如:
{{ url_for('user.login') }}

模板中應用藍圖自定義路徑的靜態檔案:

{{ url_for('建立藍圖時第一個引數.static',filename='藍圖自定義靜態檔案路徑下的檔案') }}
# 如:
{{ url_for('user.static',filename='login.css') }}

3.3.5 藍圖子域名的實現

建立藍圖的時候填寫subdomain可以指定子域名,可以參考之前註冊路由中實現子域名。

(1) 配置C:\Windows\System32\drivers\etc\hosts

127.0.0.1 bookmanage.com
127.0.0.1 admin.bookmanage.com

(2)給app增加配置

app.config['SERVER_NAME'] = 'bookmanage.com:5000'

(3)建立藍圖的時候新增子域名 subdomain='admin'

# 1 建立藍圖                                           
user_bp = Blueprint('user',__name__,url_prefix='/user',template_folder='views_templates',
                    static_folder='views_static',subdomain='admin')


# 2 利用藍圖建立路由關係
@user_bp.route('/login/')
def login():
    return render_template('login_master.html')

(4) 訪問admin.bookmanage.com:5000/user/login/

3.3.6 藍圖中使用自己請求擴充套件

在藍圖中可以利用建立好的藍圖物件,設定訪問藍圖的檢視函式的時候觸發藍圖獨有的請求擴充套件。

例如:

order_bp = Blueprint('order',__name__)


@order_bp.route('/add_order/')
def add_order():
    return "add_order"

@order_bp.before_request
def order_bp_before_request():
    return '請登入'

注意:

  • 只有訪問該藍圖下的檢視函式時候才會觸發該藍圖的請求擴充套件。
  • 可以這麼理解:相當app的請求擴充套件是全域性的,而藍圖的請求擴充套件是區域性的只對本藍圖下的檢視函式有效。

3.3 使用藍圖之中小型系統

目錄結構:

-flask_small_pro
	-app01
    	-__init__.py
    	-static
        -templates
        -views
        	-order.py
            -user.py
     -manage.py 
        

init.py

from flask import Flask
from app01.views.user import user_bp
from app01.views.order import order_bp


app = Flask(__name__)

app.register_blueprint(user_bp,url_prefix='/user')
app.register_blueprint(order_bp)

user.py

from flask import Blueprint

user_bp = Blueprint('user',__name__)


@user_bp.route('/login/')
def login():
    return 'login'

@user_bp.route('/logout/')
def logout():
    return 'logout'

order.py

from flask import Blueprint

order_bp = Blueprint('order',__name__)

@order_bp.route('/add_order/')
def add_order():
    return 'buy_order'


@order_bp.route('/modify_order/')
def modify_order():
    return 'modify_order'

manage.py

from app01 import app


if __name__ == '__main__':
    app.run()

3.4 使用藍圖之使用大型系統

這裡所謂的大型系統並不是絕對的大型系統,而是相對規整的大型系統,相當於提供了一個參考,在真實的生成環境中會根據公司的專案以及需求,規劃自己的目錄結構。

檔案路徑:

│  run.py  
│
│
└─pro_flask  # 資料夾
    │  __init__.py 
    │
    ├─admin	 # 資料夾
    │  │  views.py
    │  │  __init__.py
    │  │
    │  ├─static # 資料夾
    │  └─templates  # 資料夾
    │
    └─web	# 資料夾
       │  views.py
       │  __init__.py
       │
       ├─static  # 資料夾
       └─templates # 資料夾
    
    

run.py 啟動app

from pro_flask import app

if __name__ == '__main__':
    app.run()

__init__.py 例項化核心類,匯入藍圖物件,註冊藍圖。

from flask import Flask
from .admin import admin
from .web import web

app = Flask(__name__)
app.debug = True

app.register_blueprint(admin, url_prefix='/admin')
app.register_blueprint(web)

admin.views.py 完成註冊路由以及檢視函式

from . import admin


@admin.route('/index')
def index():
    return 'Admin.Index'

admin.__init__.py 生成藍圖物件匯入views,使得views的程式碼執行完成註冊路由

from flask import Blueprint

admin = Blueprint(
    'admin',
    __name__,
    template_folder='templates',
    static_folder='static'
)
from . import views

web資料夾下和admin資料夾下目錄構成完全一致,這裡就不舉例子了。