藍圖與視圖¶
視圖函式是您編寫的程式碼,用於回應對應用程式的請求。Flask 使用模式將傳入的請求 URL 與應處理它的視圖進行匹配。視圖返回 Flask 轉換為傳出回應的資料。Flask 也可以反向操作,根據視圖的名稱和引數產生視圖的 URL。
建立藍圖¶
Blueprint
是一種組織相關視圖和其他程式碼的方式。視圖和其他程式碼不是直接向應用程式註冊,而是向藍圖註冊。然後,當藍圖在工廠函式中可用時,將藍圖向應用程式註冊。
Flaskr 將有兩個藍圖,一個用於身份驗證功能,另一個用於部落格文章功能。每個藍圖的程式碼將放在一個單獨的模組中。由於部落格需要了解身份驗證,您將首先編寫身份驗證藍圖。
flaskr/auth.py
¶import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from flaskr.db import get_db
bp = Blueprint('auth', __name__, url_prefix='/auth')
這會建立一個名為 'auth'
的 Blueprint
。與應用程式物件一樣,藍圖需要知道它的定義位置,因此將 __name__
作為第二個引數傳遞。url_prefix
將被添加到與藍圖關聯的所有 URL 的前面。
使用 app.register_blueprint()
從工廠匯入和註冊藍圖。將新程式碼放在工廠函式的末尾,然後再傳回應用程式。
flaskr/__init__.py
¶def create_app():
app = ...
# existing code omitted
from . import auth
app.register_blueprint(auth.bp)
return app
身份驗證藍圖將具有註冊新使用者以及登入和登出的視圖。
第一個視圖:註冊¶
當使用者訪問 /auth/register
URL 時,register
視圖將傳回 HTML,其中包含供他們填寫的表單。當他們提交表單時,它將驗證他們的輸入,並顯示包含錯誤訊息的表單,或建立新使用者並轉到登入頁面。
現在您只需編寫視圖程式碼。在下一頁,您將編寫範本以產生 HTML 表單。
flaskr/auth.py
¶@bp.route('/register', methods=('GET', 'POST'))
def register():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
db = get_db()
error = None
if not username:
error = 'Username is required.'
elif not password:
error = 'Password is required.'
if error is None:
try:
db.execute(
"INSERT INTO user (username, password) VALUES (?, ?)",
(username, generate_password_hash(password)),
)
db.commit()
except db.IntegrityError:
error = f"User {username} is already registered."
else:
return redirect(url_for("auth.login"))
flash(error)
return render_template('auth/register.html')
以下是 register
視圖函式正在執行的操作
@bp.route
將 URL/register
與register
視圖函式關聯起來。當 Flask 接收到對/auth/register
的請求時,它將呼叫register
視圖並使用傳回值作為回應。如果使用者提交了表單,
request.method
將為'POST'
。在這種情況下,開始驗證輸入。request.form
是一種特殊的dict
類型,用於映射提交的表單鍵和值。使用者將輸入他們的username
和password
。驗證
username
和password
不為空。如果驗證成功,則將新使用者資料插入資料庫。
db.execute
接受一個 SQL 查詢,其中包含?
佔位符,用於任何使用者輸入,以及一個值組,用於替換佔位符。資料庫函式庫將負責轉義這些值,因此您不會受到SQL 注入攻擊的影響。為了安全起見,密碼永遠不應直接儲存在資料庫中。相反,
generate_password_hash()
用於安全地雜湊密碼,並儲存該雜湊值。由於此查詢會修改資料,因此需要之後呼叫db.commit()
以儲存變更。如果使用者名稱已存在,則會發生
sqlite3.IntegrityError
,這應作為另一個驗證錯誤顯示給使用者。
儲存使用者後,他們將被重新導向到登入頁面。
url_for()
根據登入視圖的名稱產生其 URL。這比直接編寫 URL 更可取,因為它允許您稍後更改 URL,而無需更改連結到它的所有程式碼。redirect()
產生對產生 URL 的重新導向回應。如果驗證失敗,則會向使用者顯示錯誤。
flash()
儲存可以在呈現範本時檢索的訊息。當使用者最初導航到
auth/register
,或存在驗證錯誤時,應顯示包含註冊表單的 HTML 頁面。render_template()
將呈現包含 HTML 的範本,您將在教學的下一步中編寫該範本。
登入¶
此視圖遵循與上面的 register
視圖相同的模式。
flaskr/auth.py
¶@bp.route('/login', methods=('GET', 'POST'))
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
db = get_db()
error = None
user = db.execute(
'SELECT * FROM user WHERE username = ?', (username,)
).fetchone()
if user is None:
error = 'Incorrect username.'
elif not check_password_hash(user['password'], password):
error = 'Incorrect password.'
if error is None:
session.clear()
session['user_id'] = user['id']
return redirect(url_for('index'))
flash(error)
return render_template('auth/login.html')
與 register
視圖相比,有幾個不同之處
首先查詢使用者並將其儲存在變數中以供稍後使用。
fetchone()
從查詢中傳回一行。如果查詢未傳回任何結果,則傳回None
。稍後,將使用fetchall()
,它會傳回所有結果的列表。check_password_hash()
以與儲存的雜湊相同的方式雜湊提交的密碼,並安全地比較它們。如果它們匹配,則密碼有效。session
是一個dict
,用於跨請求儲存資料。當驗證成功時,使用者的id
會儲存在新的 session 中。資料儲存在cookie 中,該 cookie 會傳送到瀏覽器,然後瀏覽器會在後續請求中將其傳回。Flask 安全地簽署資料,使其不會被篡改。
現在使用者的 id
儲存在 session
中,它將在後續請求中可用。在每個請求開始時,如果使用者已登入,則應載入其資訊並使其可供其他視圖使用。
flaskr/auth.py
¶@bp.before_app_request
def load_logged_in_user():
user_id = session.get('user_id')
if user_id is None:
g.user = None
else:
g.user = get_db().execute(
'SELECT * FROM user WHERE id = ?', (user_id,)
).fetchone()
bp.before_app_request()
註冊一個在視圖函式之前執行的函式,無論請求的 URL 是什麼。load_logged_in_user
檢查使用者 ID 是否儲存在 session
中,並從資料庫中取得該使用者的資料,將其儲存在 g.user
上,這會持續到請求結束。如果沒有使用者 ID,或者 ID 不存在,則 g.user
將為 None
。
登出¶
要登出,您需要從 session
中移除使用者 ID。然後 load_logged_in_user
將不會在後續請求中載入使用者。
flaskr/auth.py
¶@bp.route('/logout')
def logout():
session.clear()
return redirect(url_for('index'))
在其他視圖中要求身份驗證¶
建立、編輯和刪除部落格文章將需要使用者登入。可以使用裝飾器來為應用它的每個視圖檢查這一點。
flaskr/auth.py
¶def login_required(view):
@functools.wraps(view)
def wrapped_view(**kwargs):
if g.user is None:
return redirect(url_for('auth.login'))
return view(**kwargs)
return wrapped_view
此裝飾器傳回一個新的視圖函式,該函式包裝了應用它的原始視圖。新函式檢查是否已載入使用者,否則重新導向到登入頁面。如果已載入使用者,則會呼叫原始視圖並正常繼續。您將在編寫部落格視圖時使用此裝飾器。
端點和 URL¶
url_for()
函式根據名稱和引數產生視圖的 URL。與視圖關聯的名稱也稱為端點,預設情況下,它與視圖函式的名稱相同。
例如,教學稍早添加到應用程式工廠的 hello()
視圖的名稱為 'hello'
,可以使用 url_for('hello')
連結到它。如果它接受一個引數(您稍後會看到),則可以使用 url_for('hello', who='World')
連結到它。
當使用藍圖時,藍圖的名稱會添加到函式名稱的前面,因此您上面編寫的 login
函式的端點為 'auth.login'
,因為您將其添加到 'auth'
藍圖。
繼續閱讀 範本。