前言
- 在前面的例子中,所有的頁面處理邏輯都是放在同一個文件中,隨着業務代碼的增加,將所有代碼都放在單個程序文件中是非常不合適的
- 不僅會讓閱讀代碼變得困難,而且會給后期維護帶來麻煩
- Flask 中使用藍圖,提供了模塊化管理程序路由的功能,使程序結構更加清晰
藍圖簡介
- 隨着 Flask 程序越來越復雜,需要對程序進行模塊化的處理
- 藍圖 (Blueprint) 是 Flask 程序的模塊化處理機制
- 它是一個存儲視圖方法的集合
- Flask 程序通過 Blueprint 來組織 URL 以及處理請求
Blueprint 具有以下屬性
- 一個項目可以具有多個 Blueprint
- Blueprint 可以單獨擁有自己的模板、靜態文件的目錄
- 在應用初始化時,注冊需要使用的 Blueprint
基本用法
功能概述
假設網站包含有如下 4 個頁面:
頁面 | 功能 | 處理函數 |
---|---|---|
/news/society/ | 社會新聞版塊 | society_news |
/news/tech/ | IT 新聞版塊 | tech_news |
/products/car/ | 汽車產品版塊 | car_products |
/products/baby/ | 嬰幼兒產品版塊 | baby_products |
- 前兩個都是 /news 前綴,可以組成一個藍圖 news
- 后兩個是 /products 前綴,可以組成一個藍圖 products
- 相當於四個視圖函數,兩個藍圖
程序中包含 4 個視圖函數,根據頁面路徑,Flask 將請求轉發給對應的視圖函數,從瀏覽器發送過來的請求的處理過程如下圖所示
使用藍圖后,路由匹配流程
- 瀏覽器訪問路徑 /products/car
- Flask 框架在藍圖 news 和藍圖 products 中查找匹配該頁面路徑的路由
- 發現在藍圖 products 中,存在和路徑 /products/car 匹配的視圖函數 car_products
- 最后將請求轉發給函數 car_products 處理
實戰小栗子
目錄結構
例子程序包括 2 個藍圖,由 3 個文件構成:
app.py
,程序的主文件;news.py
,實現藍圖 news;products.py
,實現藍圖 products。
app.py 代碼
#!usr/bin/env python # -*- coding:utf-8 _*- """ # author: 小菠蘿測試筆記 # blog: https://www.cnblogs.com/poloyy/ # time: 2021/7/12 8:36 下午 # file: app.py """ # 導入 Flask 和 藍圖 Blueprint from flask import Flask, Blueprint # 導入藍圖類 from s7_blueprints import news from s7_blueprints import products app = Flask(__name__) # 注冊藍圖 app.register_blueprint(news.blueprint) app.register_blueprint(products.blueprint) if __name__ == '__main__': app.run(debug=True)
news.py 代碼
#!usr/bin/env python # -*- coding:utf-8 _*- """ # author: 小菠蘿測試筆記 # blog: https://www.cnblogs.com/poloyy/ # time: 2021/7/12 8:36 下午 # file: news.py """ # 導入藍圖 from flask import Blueprint """ 實例化藍圖對象 第一個參數:藍圖名稱 第二個參數:導入藍圖的名稱 第三個參數:藍圖前綴,該藍圖下的路由規則前綴都需要加上這個 """ blueprint = Blueprint('news', __name__, url_prefix="/news") # 用藍圖注冊路由 @blueprint.route("/society/") def society_news(): return "社會新聞板塊" @blueprint.route("/tech/") def tech_news(): return "新聞板塊"
注意:頁面的絕對路徑是 /news/society/ 和 /news/tech/,因為藍圖的 url_prefix 設置為 news,在藍圖內部,頁面的相對路徑是 /society/ 和 /tech/
products.py 代碼
#!usr/bin/env python # -*- coding:utf-8 _*- """ # author: 小菠蘿測試筆記 # blog: https://www.cnblogs.com/poloyy/ # time: 2021/7/12 8:36 下午 # file: products.py """ from flask import Blueprint blueprint = Blueprint("products", __name__, url_prefix="/product") @blueprint.route("/car") def car_products(): return "汽車產品版塊" @blueprint.route("/baby") def baby_products(): return "嬰兒產品版塊"
注意:頁面的絕對路徑是 /products/car/ 和 /product/baby/,因為藍圖的 url_prefix 等於 products,在藍圖內部,頁面的相對路徑是 /car/ 和 /baby/
postman 發起請求的結果
更具擴展性的架構
概述
隨着業務代碼的增加,需要為 Flask 程序提供一個具備擴展性的架構,根據 Flask 程序的擴展性分為如下三種類型:
1、所有的頁面邏輯放在同一個文件中
在這種架構中,程序完全不具備擴展性
在初學 Flask 時,使用的栗子都是這種類型
2、使用一個獨立的 Python 文件實現藍圖
在這種架構中,程序具備一定的擴展性:
- 程序由主程序和多個藍圖構成
- 每個藍圖對應一個 Python 文件
- 所有的藍圖共享相同的模板文件目錄
- 所有的藍圖共享相同的靜態文件目錄
上面的栗子就是采用這種架構
程序包含 2 個藍圖: news 和 products,由 3 個文件構成:app.py
、news.py
、products.py
,其中 news.py
實現新聞版塊,products.py
實現產品版塊
3、使用一個獨立的目錄實現藍圖
在這種架構中,程序的擴展性最好:
- 程序由主程序和多個藍圖構成
- 每個藍圖對應一個獨立的目錄,存儲與這個藍圖相關的文件
- 每個藍圖有一個獨立的模板文件目錄
- 每個藍圖有一個獨立的靜態文件目錄
模板文件尋找規則
每個藍圖可以擁有獨立的模板文件目錄,模板文件尋找規則如下:
- 如果項目中的 templates 文件夾中存在相應的模板文件,則使用 templates 文件夾下的模板文件;
- 如果項目中的 templates 文件夾中沒有相應的模板文件,則使用定義藍圖的時候指定的 templates 文件夾下的模板文件
- 項目中的 templates 文件夾優先級大於指定的 templates 文件夾
靜態文件尋找規則
每個藍圖可以獨立的靜態文件目錄,靜態文件尋找規則如下:
- 如果項目中的 static 文件夾中存在相應的靜態文件,則使用 static 文件夾下的靜態文件
- 如果項目中的 static 文件夾中沒有相應的靜態文件,則使用定義藍圖的時候指定的 static 文件夾下的靜態文件
- 項目中的 templates 文件夾優先級大於指定的 templates 文件夾
究極擴展性的栗子
目錄結構
目錄功能描述
路徑 | 功能描述 |
---|---|
templates | 項目默認的模板文件夾 |
static | 項目默認的靜態文件夾 |
news | 藍圖 news 的相關文件 |
news/templates | 藍圖 news 的私有模板文件夾 |
news/static | 藍圖 news 的私有靜態文件夾 |
products | 藍圖 products 的相關文件 |
products/templates | 藍圖 products 的私有模板文件夾 |
products/static | 藍圖 products 的私有靜態文件夾 |
文件功能描述
路徑 | 功能描述 |
---|---|
app.py |
主程序 |
news/__init.py__ | 藍圖 news 的實現 |
news/templates/society.html | 屬於藍圖 news 的一個模板文件 |
news/static/news.css | 屬於藍圖 news 的一個靜態文件 |
products/__init.py__ | 藍圖 products 的實現 |
app.py 的代碼
#!usr/bin/env python # -*- coding:utf-8 _*- """ # author: 小菠蘿測試筆記 # blog: https://www.cnblogs.com/poloyy/ # time: 2021/7/12 10:39 下午 # file: 7_blueprint_app.py """ from flask import Flask from s7_news import news from s7_product import products app = Flask(__name__) app.register_blueprint(news.blueprint) app.register_blueprint(products.blueprint) app.run(debug=True)
news/__init.py__ 的代碼
#!usr/bin/env python # -*- coding:utf-8 _*- """ # author: 小菠蘿測試筆記 # blog: https://www.cnblogs.com/poloyy/ # time: 2021/7/12 8:36 下午 """ # 導入藍圖 from flask import Blueprint, render_template """ 實例化藍圖對象 第一個參數:藍圖名稱 第二個參數:導入藍圖的名稱 第三個參數:藍圖前綴,該藍圖下的路由規則前綴都需要加上這個 """ blueprint = Blueprint('news', __name__, url_prefix="/news", template_folder="templates", static_folder="static") # 用藍圖注冊路由 @blueprint.route("/society/") def society_news(): return render_template('society.html') @blueprint.route("/tech/") def tech_news(): return "IT 新聞板塊"
- 藍圖中頁面的 URL 前綴為 /news;
- 藍圖的模板目錄為 templates,絕對路徑為 ‘項目目錄 /news/templates’;
- 藍圖的靜態文件目錄為 static,絕對路徑為 ‘項目目錄 /news/static’
- 調用 render_template (‘society.html’) 渲染模板文件 society.html,根據模板文件的查找規則,最終在 ‘項目目錄 /news/templates’ 目錄下找到模板文件
news/templates/society.html 的代碼
<link rel="stylesheet" href="{{ url_for('news.static',filename='news.css')}}"> <h1>社會新聞</h1>
在模板文件中引用了靜態文件 news.css。{{url_for (‘news.static’,filename=‘news.css’) }} 的輸出為 news/static/news.css,其中 news.static 表示藍圖 news 的 static 目錄
news/static/news.css 的代碼
h1 { color: red; }
product.py/__init.py__ 的代碼
from flask import Blueprint blueprint = Blueprint('products', __name__, url_prefix='/products') @blueprint.route("/car") def car_products(): return "汽車產品版塊" @blueprint.route("/baby") def baby_products(): return "嬰兒產品版塊"
瀏覽器訪問效果
訪問 http://localhost:5000/news/society/
驗證目錄優先級
在根目錄下的 templates 目錄下也添加一個 society.html 文件,在根目錄下的 static 目錄下添加一個 project.css
html 代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <link rel="stylesheet" href="{{ url_for('static',filename='project.css') }}"> <h1>社會新聞啊啊啊</h1> </body> </html>
css 代碼
h1 {
color: blue;
}
預期結果
- 根據 templates、static 的查找規則,會優先查找項目根目錄的 templates、static 目錄下是否有對應的模板文件、靜態文件
- 這里 society.html 同時出現在根目錄的 templates 和藍圖目錄的 templates,應該優先返回根目錄的 templates 下的 society.html
瀏覽器訪問效果
符合預期結果
Blueprint 源碼解析
類初始化 __init__ 方法參數列表
- name:藍圖名稱,將會被添加到每個 endpoint
- import_name:藍圖包的名稱,通常是 __name__,有助於找到 root_path 藍圖
- static_folder:包含靜態文件的文件夾,由藍圖的靜態路由提供服務,路徑以藍圖文件為根路徑開始找
- static_url_path:提供靜態文件的 url,默認就是 static_folder,如果藍圖沒有 url_prefix,應用程序的靜態路由將優先,並且藍圖的靜態文件將無法訪問
- template_folder:包含模板文件的文件夾,路徑以藍圖文件為根路徑開始找
- url_prefix:會作為藍圖所有路由的前綴路徑
- subdomain:藍圖路由將匹配的子域
- url_defaults:藍圖路由的默認值字典
- root_path:默認情況下,藍圖會自動設置這基於“import_name”