flask實戰-個人博客-使用藍本模塊化程序
使用藍本模塊化程序
實例化flask提供的blueprint類就創建一個藍本實例。像程序實例一樣,我們可以為藍本實例註冊路由、錯誤處理函數、上下文處理函數,請求處理函數,甚至是單獨的靜態文件文件夾和模板文件夾。在使用上,它和程序實例也很相似。比如,藍本實例同樣擁有一個route()裝飾器,可以用來註冊路由,但實際上藍本對象和程序對象卻有很大的不同。
實例化Blueprint類時,除了傳入構造函數的第一個參數是藍本名稱之外,創建藍本實例和使用flask對象創建程序實例的代碼基本相同。例如,下面的代碼創建了一個blog藍本:
from flask import Blueprint blog= Blueprint(‘blog‘, __name__)
使用藍本不僅僅是對視圖函數分類,而是將程序某一部分的所有操作組織在一起。這個藍本實例以及一系列註冊在藍本實例上的操作的集合被稱為一個藍本。你可以把藍本想象成模子,它描述了程序某一部分的細節,定義了相應的路由、錯誤處理器、上下文處理器、請求處理器等一系列操作。但是它本身卻不能發揮作用,因為它只是一個模子。只有當你把它註冊到程序上時,它才會把物體相應的部分印刻出來----把藍本中的操作附加到程序上。
使用藍本可以將程序模塊化(modular)。一個程序可以註冊多個藍本,我們可以把程序按照功能分離成不同的組件,然後使用藍本來組織這些組件。藍本不僅僅是在代碼層面上的組織程序,還可以在程序層面上定義屬性,具體的形式即為藍本下的所有路由設置不同的URL前綴或子域名。
舉一個常見的例子,為了讓移動設備擁有更好的體驗,我們為移動設備創建了單獨的視圖函數,這部分視圖函數可以使用單獨的mobile藍本註冊,然後為這個藍本設置子域名m。用戶訪問m.example.com的請求會自動被該藍本的視圖函數處理。
1、創建藍本
藍本一般在子包中創建,比如創建一個blog子包,然後再構造文件中創建藍本實例,使用包管理藍本允許你設置藍本獨有的靜態文件和模板,並在藍本內對各類函數分模塊存儲。
在簡單的程序中,我們也可以直接在模塊中創建藍本實例。根據程序的功能,我們分別創建了三個腳本:auth.py(用戶認證)、blog.py(博客前臺)、admin.py(博客後臺),分別存儲各自藍本的代碼。以auth.py為例,藍本實例auth_bp在auth.py腳本頂端創建:
from flask import Blueprint auth_bp = Blueprint(‘auth‘, __name__)
在藍本對象的名稱後添加一個_bp後綴(即blueprint的簡寫)並不是必須的,這裏是為了更容易區分藍本對象,而且可以避免潛在的命名沖突。
在上面的代碼中,我們從Flask導入Blueprint類,實例化這個類就獲得了我們的藍本對象。構造方法中的第一個參數是藍本的名稱;第二個參數是包或模塊的名稱,我們可以使用__name__變量。Blueprint類的構造函數還接收其他參數來定義藍本,我們會在後面進行學習。
2、裝配藍本
藍本實例時一個用於註冊路由等操作的臨時對象。這一節我們來學習在藍本上可以註冊哪些操作,以及其中的一些細節。
我們在下面介紹的方法和屬性都是在表示藍本的Blueprint類中定義的,因此可以通過我們的藍本實例調用,在提及這些方法和屬性時,我們會省略掉類名稱,比如Blueprint.route()會寫為route()。
1)視圖函數
藍本中的視圖函數通過藍本實例提供的route()裝飾器註冊,即auth_bp.route()。我們把和認證相關的視圖函數移動到這個模塊,然後註冊到auth藍本上,如下所示:
from flask import Blueprint auth_bp = Blueprint(‘auth‘, __name__) @auth_bp.route(‘/login‘, methods=[‘GET‘, ‘POST‘]) def login(): if current_user.is_authenticated: return redirect(url_for(‘blog.index‘)) form = LoginForm() if form.validate_on_submit(): username = form.username.data password = form.password.data remembet =form.remember.data admin = Admin.query.first() if admin: if username == admin.username and admin.validate_password(password): login_user(admin, remember) flash(‘Welcom back.‘, ‘info‘) return redirect_back() flash(‘Invalid username or password.‘, ‘warning‘) else: flash(‘No account.‘, ‘Warning‘) return render_template(‘auth/login.html‘, form=form) @auth_bp.route(‘/logout‘) @login_required def logout(): logout_user() flash(‘Logout success.‘, ‘info‘) return redirect_back()
現在的auth.py腳本就像一個完整的單腳本Flask程序,和之前練習的例子非常相似。
2)錯誤處理函數
使用藍本實例的errorhandler()裝飾器可以把錯誤處理器註冊到藍本上,這些錯誤處理器只會捕捉訪問該藍本中的路由發生的錯誤;使用藍本實例的app_errorhandler()裝飾器則可以註冊一個全局的錯誤處理器。
404和405錯誤僅會被全局的錯誤處理函數捕捉,如果你想區分藍本URL下的404和405錯誤,可以在全局定義的404錯誤處理函數中使用request.path.startswith(‘<藍本的URL前綴>’)來判斷請求的URL是否屬於某個藍本。下面我們會介紹如何為藍本設置URL前綴。
3)請求處理函數
在藍本中,使用before_request、after_request、teardown_request等裝飾器註冊的請求處理函數時藍本獨有的,也就是說,只有該藍本中的視圖函數對應的請求才會觸發相應的請求處理函數。另外,在藍本中也可以使用before_app_request、after_app_request、teardown_app_request、before_app_first_request方法,這些方法註冊的請求處理函數是全局的。
4)模板山下文處理函數
和請求鉤子類似,藍本實例可以使用context_processor裝飾器註冊藍本特有的模板上下文處理器;使用app_context_processor裝飾器則會註冊程序全局的模板上下文*處理器。
另外,藍本對象也可以使用app_template_global()、app_template_filter()和app_template_test()裝飾器,分別用來註冊全局的模板全局函數、模板過濾器和模板測試器。
並不是所有程序實例提供的方法和屬性都可以在藍本對象上調用,藍本對象只提供了少量用於註冊處理函數的方法,大部分的屬性和方法我們仍然需要通過程序實例獲取,比如表示配置的config屬性,或是註冊自定義命令的cli.command()裝飾器。
3、註冊藍本
我們在本章開始曾把藍本比喻成模子,為了讓這些模子發揮作用,我們需要把藍本註冊到程序實例上:
from personalBlog.blueprints.auth import auth_bp def register_blueprints(app): app.register_blueprint(auth_bp)
藍本使用Flask.register_blueprint()方法註冊,必須傳入的參數是我們在上面創建的藍本對象。其他的參數可以用來控制藍本的行為。比如,我們使用url_prefix參數為auth藍本下的所有視圖URL附加一個URL前綴:
def register_blueprints(app): app.register_blueprint(auth_bp, url_prefix=‘/auth‘)
這時,auth藍本下的視圖的URL前都會添加一個/auth前綴,比如login視圖的URL規則會變為/auth/login。使用subdomain參數可以為藍本下的路由設置子域名。比如,下面藍本中的所有視圖會匹配來自auth子域的請求:
app.register_blueprint(auth_bp, subdomain=‘auth‘)
這時訪問auth.example.com/login的URL才會觸發auth藍本中的login視圖。
register_blueprint()方法接收的額外參數和Blueprint類的構造方法基本相同,在這裏傳入的參數會覆蓋傳入藍本構造方法的參數。
4、藍本的路由端點
端點作為URL規則和視圖函數的中間媒介,是我們之前介紹url_for()函數時提及的概念。
(端點:用來標記一個視圖函數以及對應的url規則,就是端點。端點的默認值是視圖函數的名稱)
下面先來深入了解一下端點,我們使用app.route()裝飾器將視圖函數註冊為路由:
@app.route(‘/hello‘) def say_hello(): return "Hello!"
如果你沒有接觸過裝飾器,可能會感到很神秘,其實它只是一層包裝而已。如果不用app.route()裝飾,使用app.add_url_rule()方法同樣也可以註冊路由:
def say_hello(): return ‘Hello!‘ app.add_url_rule(‘/hello‘, ‘say_hello‘, say_hello)
add_url_rule(rule, endpoint, view_func)的第二個參數即指定的端點(endpoint),第三個參數是視圖函數對象。
在路由裏,URL規則和視圖函數並不是直接映射的,而是通過端點作為中間媒介。類似這樣:
/hello (URL規則) -> say_hello(端點) -> say_hello(視圖函數)
默認情況下,端點是視圖函數的名稱,在這裏即say_hello。我們也可以顯示地使用endpoint參數改變它:
@app.route(‘/hello‘, endpoint = ‘give_greeting‘) def say_hello(): return ‘Hello!‘
這時端點變成了give_greeting,映射規則也相應改變:
/hello (URL規則) -> give_greeting (端點) -> say_hello (視圖函數)
現在使用flask routes命令查看當前註冊的所有路由:
(personalBlog-2ooe7Iqy) D:\flask\personalBlog>flask routes
Endpoint Methods Rule
----------------------------- --------- --------------------------------------------
auth.login GET, POST /auth/login
auth.logout GET /auth/logout
blog.about GET /about
從上面的輸出可以到,每個路由的URL規則(Rule)對應的端點(Endpoint)值不再僅僅是視圖函數名,而是“藍本命.視圖函數名”的形式(這裏的藍本命即我們實例化Blueprint類時傳入的第一個參數)。前面我們留下了一個疑問:為什麽不直接映射URL規則到視圖函數呢?現在是揭曉答案的時候了。答案就是---使用端點可以實現藍本的視圖函數命名空間。
當使用藍本時,你可能會在不同的藍本中創建同名的視圖函數。比如,在兩個藍本中都有一個index視圖,這時在模板中使用url_for()獲取URL時,因為填入的端點參數值是視圖函數的名稱,就會產生沖突。flask在端點前添加藍本的名稱,擴展了端點的命名空間,解決了視圖函數重名的問題。正因為這樣,一旦使用藍本,我們就要對程序中所有的url_for()函數中的端點值進行修改,添加藍本命來明確端點的歸屬。比如,在生成auth藍本下的login視圖的URL時,需要使用下面的端點:
url_for(‘auth.login’)
端點也有一種簡寫的方式,在藍本內部可以使用“.視圖函數名”的形式來省略藍本名稱,比如“auth.login”可以簡寫為“.login”。但是在全局環境中,比如在多個藍本中都要使用的基模板,或是在A藍本中的腳本或渲染的模板中需要生成B藍本的URL,這時的端點值則必須使用完整的名稱。
使用藍本本可以避免端點值的重復沖突,但是路由的URL規則還是會產生重復。比如,兩個藍本中的主頁視圖的URL規則都是“/home”,當在瀏覽器中訪問這個地址時,請求只會分配到第一個註冊的藍本中的主頁視圖。為了避免這種情況,可以在註冊藍本時使用關鍵字參數url_prefix在藍本的URL規則前添加一個URL前綴來解決。
一個藍本可以註冊多次,有時你需要讓程序在不同的URL規則下都可以訪問,這時就可以為同一個藍本註冊多次,每次設置對應的URL前綴或子域名。
5、藍本資源
如果程序的不同藍本的頁面需要截然不同的樣式,可以為藍本定義獨有的靜態文件和模板。這時我們需要把藍本升級為包,在構造文件中創建藍本實例,並在藍本包中創建靜態文件夾static和模板文件夾templates。和程序實例一樣,實例化時傳入的__name__變量會被用來判斷藍本的根目錄,並以此作為基礎尋找模板文件夾和靜態文件文件夾。
有時,你引入藍本的唯一目的就是用來提供資源文件。比如,提供內置本地資源的擴展會使用剛註冊藍本的形式提供靜態文件和模板。
要使用藍本獨有的靜態文件,你需要在定義藍本時使用static_folder關鍵字指定藍本的靜態文件文件夾的路徑:
auth_bp = Blueprint(‘auth‘, __name__, static_folder = ‘static‘, static_url_path=‘/auth/static‘)
這個參數的值可以是絕對路徑或相對於藍本所在文件夾的相對路徑。另外,因為藍本內置的static路由的URL規則和程序的static路由的URL規則相同,都是“/static”,為了避免沖突,我們使用個可選的static_url_path參數為藍本下的static指定了新的 URL規則。
如果你在註冊藍本時為藍本定義了URL前綴,即設置了url_prefix參數,那麽最終的藍本靜態文件路徑會自動設為“/藍本前綴/static”,這時可以省略static_url_path的定義。
在生成用來獲取藍本靜態文件的URL時需要寫出包含藍本名稱的完整端點,即“藍本命.static”,下面的調用會返回“admin/static/style.css”:
url_for(‘admin.static‘, filename = ‘style.css‘)
當籃本包含獨有的模板文件夾時,我們可以在實例化藍本類時使用template_folder關鍵字指定模板文件夾的位置:
admin = Blueprint(‘admin‘, __name__, template_folder = ‘templates‘)
當我們在藍本中的視圖函數渲染一個index.html模板時,Flask會優先從全局的模板文件夾中尋找,如果沒有找到,再到 藍本所在的模板文件夾查找。因此,為了避免藍本的模板文件夾中 和全局模板文件夾中存在同名文件導致沖突,通常會在藍本的模板文件夾中以籃板名稱新建一個子文件夾存儲模板。
如果藍本之間的關聯比較大,通用同一個基模板,更常見的方法是只在全局的模板文件夾中存儲模板,在其中可以建立子文件夾來進行住址;靜態文件的處理方式也一樣。
flask實戰-個人博客-使用藍本模塊化程序