【Flask】官方教程(Tutorial)-part2:蓝图-视图、模板、静态文件
前序文章:
官方教程(Tutorial)-part1:項目布局、應用程序設置、定義和訪問數據庫
藍圖-視圖
視圖函數是您為響應應用程序請求而編寫的代碼。Flask 使用模式將傳入的請求 URL 與應該處理它的視圖相匹配。視圖返回 Flask 轉換為傳出響應的數據。 Flask 也可以換一種方式,根據視圖的名稱和參數生成視圖的 URL。
創建一個藍圖
藍圖是一種組織一組相關視圖和其他代碼的方式。與其將視圖和其他代碼直接注冊到應用程序,不如將它們注冊到藍圖。然后藍圖在工廠函數中可用時向應用程序注冊。
Flaskr 將有兩個藍圖,一個用于身份驗證功能,一個用于博客文章功能。每個藍圖的代碼將放在一個單獨的模塊中。由于博客需要了解身份驗證,因此將首先編寫身份驗證。
編寫flaskr/auth.py腳本。
import functoolsfrom flask import (Blueprint, flash, g, redirect, render_template, request, session, url_for ) from werkzeug.security import check_password_hash, generate_password_hashfrom flaskr.db import get_dbbp = Blueprint('auth', __name__, url_prefix='/auth')上面創建了一個名為auth的藍圖。類似于應用對象,藍圖也是需要知道在哪里創建的,所以需要傳入__name__作為第二個參數。至于url_prefix將添加到與藍圖關聯的所有 URL。
使用 app.register_blueprint() 從工廠導入并注冊藍圖。在返回應用程序之前,將新代碼放在工廠函數(flaskr/__init__.py)的末尾。如下:
def create_app():app = ...# existing code omittedfrom . import authapp.register_blueprint(auth.bp)return app身份驗證藍圖將具有注冊新用戶以及登錄和注銷的視圖。
第一個視圖:注冊
當用戶訪問/auth/registerURL鏈接時,注冊視圖將會返回供其填寫的form表單html。當他們提交這個表單是,它將驗證他們的輸入并再次顯示帶有錯誤消息的表單或創建新用戶并轉到登錄頁面。對應的視圖代碼如下:
flaskr/auth.py
視圖函數功能工作:
- db.execute使用 ? 作為 SQL 查詢任何用戶輸入的占位符,以及用于替換占位符的值元組。數據庫庫將負責轉義值,因此您不會受到 SQL 注入攻擊。
- 為了安全起見,永遠不要將密碼直接存儲在數據庫中。generate_password_hash() 用于安全地散列密碼,并存儲該散列。由于此查詢修改了數據,因此之后需要調用db.commit()來保存更改。
- 如果用戶名已經存在,則會發生 sqlite3.IntegrityError,這應該作為另一個驗證錯誤向用戶顯示。
登陸
這個功能實現的前提是,你已經把注冊完成了。代碼如下: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 = Noneuser = 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視圖不同的地方:
現在用戶的 id 存儲在會話中,它將在后續請求中可用。在每個請求開始時,如果用戶登錄,他們的信息應該被加載并提供給其他視圖。實現如下(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 = Noneelse:g.user = get_db().execute('SELECT * FROM user WHERE id = ?', (user_id,)).fetchone()bp.before_app_request() 注冊一個在視圖函數之前運行的函數,無論請求什么 URL。load_logged_in_user 檢查用戶 ID 是否存儲在會話中,并從數據庫中獲取該用戶的數據,并將其存儲在 g.user 中,該數據持續請求的長度。如果沒有用戶 id,或者 id 不存在,g.user 將為 None.
注銷登陸
要注銷,您需要從會話中刪除用戶 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') 相關聯。
使用藍圖時,藍圖的名稱會添加到函數名稱的前面,因此您在上面編寫的登錄函數的端點是“auth.login”,因為您已將其添加到“auth”藍圖中。
模板Templates
您已經為您的應用程序編寫了身份驗證視圖,但如果您正在運行服務器并嘗試訪問任何 URL,您將看到 TemplateNotFound 錯誤。那是因為視圖正在調用 render_template(),但您還沒有編寫模板。模板文件將存儲在 flaskr 包內的模板目錄中。
模板是包含靜態數據以及動態數據占位符的文件。使用特定數據呈現模板以生成最終文檔。 Flask 使用 Jinja 模板庫來渲染模板。
在您的應用程序中,您將使用模板來呈現 HTML,該 HTML 將顯示在用戶的瀏覽器中。在 Flask 中,Jinja 被配置為自動轉義 HTML 模板中呈現的任何數據。這意味著呈現用戶輸入是安全的;他們輸入的任何可能與 HTML 混淆的字符,例如 < 和 > 都將被轉義為安全值,這些值在瀏覽器中看起來相同,但不會造成不良影響。
Jinja 的外觀和行為大多類似于 Python。特殊分隔符用于區分 Jinja 語法和模板中的靜態數據。{{ 和 }} 之間的任何內容都是將輸出到最終文檔的表達式。 {% 和 %} 表示類似 if 和 for 的控制流語句。與 Python 不同,塊由開始和結束標記而不是縮進表示,因為塊內的靜態文本可能會改變縮進。
基本布局
應用程序中的每個頁面都將圍繞不同的主體具有相同的基本布局。不是在每個模板中編寫整個 HTML 結構,而是每個模板將擴展一個基本模板并覆蓋特定部分。
flaskr/templates/base.html
<!doctype html> <title>{% block title %}{% endblock %} - Flaskr</title> <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> <nav><h1>Flaskr</h1><ul>{% if g.user %}<li><span>{{ g.user['username'] }}</span><li><a href="{{ url_for('auth.logout') }}">Log Out</a>{% else %}<li><a href="{{ url_for('auth.register') }}">Register</a><li><a href="{{ url_for('auth.login') }}">Log In</a>{% endif %}</ul> </nav> <section class="content"><header>{% block header %}{% endblock %}</header>{% for message in get_flashed_messages() %}<div class="flash">{{ message }}</div>{% endfor %}{% block content %}{% endblock %} </section>g 在模板中自動可用。根據是否設置了 g.user(來自 load_logged_in_user),要么顯示用戶名和注銷鏈接,要么顯示注冊和登錄鏈接。url_for() 也是自動可用的,用于生成視圖的 URL,而不是手動寫出它們。
在頁面標題之后和內容之前,模板循環遍歷 get_flashed_messages() 返回的每條消息。您在視圖中使用了 flash() 來顯示錯誤消息,這是顯示錯誤消息的代碼。
這里定義的三個塊將在其他模板中被覆蓋:
基本模板直接位于模板目錄中。為了讓其他人保持井井有條,藍圖的模板將放置在與藍圖同名的目錄中。
注冊頁面
flaskr/templates/auth/register.html
{% extends 'base.html' %}{% block header %}<h1>{% block title %}Register{% endblock %}</h1> {% endblock %}{% block content %}<form method="post"><label for="username">Username</label><input name="username" id="username" required><label for="password">Password</label><input type="password" name="password" id="password" required><input type="submit" value="Register"></form> {% endblock %}{% extends ‘base.html’ %} 告訴 Jinja 這個模板應該替換基本模板中的塊。所有呈現的內容必須出現在覆蓋基本模板中的塊的 {% block %} 標記內。
這里使用的一個有用的模式是將 {% block title %} 放在 {% block header %} 中。這將設置標題塊,然后將其值輸出到標題塊中,以便窗口和頁面共享相同的標題,而無需兩次寫入。
input標簽在此處使用 required 屬性。這告訴瀏覽器在填寫這些字段之前不要提交表單。如果用戶使用的是不支持該屬性的舊瀏覽器,或者如果他們使用瀏覽器之外的其他東西來發出請求,您仍然需要在 Flask 視圖中驗證數據。始終完全驗證服務器上的數據很重要,即使客戶端也進行了一些驗證。
登陸頁面
除了標題和提交按鈕外,這與注冊模板相同。
flaskr/templates/auth/login.html
{% extends 'base.html' %}{% block header %}<h1>{% block title %}Log In{% endblock %}</h1> {% endblock %}{% block content %}<form method="post"><label for="username">Username</label><input name="username" id="username" required><label for="password">Password</label><input type="password" name="password" id="password" required><input type="submit" value="Log In"></form> {% endblock %}注冊一個用戶
現在驗證模板已經編寫完成,您可以注冊一個用戶。確保服務器仍在運行,然后轉到 http://target_server:5000/auth/register。
繼續在我的虛擬機上啟動服務,如下:
主機瀏覽器訪問情況如下:
填寫注冊信息提交后就到了登陸界面:
當然,可以嘗試在不填寫表格的情況下單擊“注冊”按鈕,然后查看瀏覽器顯示錯誤消息。嘗試從 register.html 模板中刪除所需的屬性,然后再次單擊“注冊”。瀏覽器不會顯示錯誤,而是會重新加載頁面并顯示視圖中來自flash()的錯誤。這里就不演示了。
填寫用戶名和密碼,您將被重定向到登錄頁面。嘗試輸入錯誤的用戶名,或正確的用戶名和錯誤的密碼。如果你登錄你會得到一個錯誤,因為還沒有索引視圖可以重定向到。
靜態文件
身份驗證視圖和模板有效,但它們現在看起來很簡單。可以添加一些 CSS 來為您構建的 HTML 布局添加樣式。樣式不會改變,所以它是一個靜態文件而不是模板。
Flask 自動添加一個靜態視圖,該視圖采用相對于 flaskr/static 目錄的路徑并為其提供服務。 base.html 模板已經有一個指向 style.css 文件的鏈接:
{{ url_for('static', filename='style.css') }}除了 CSS 之外,其他類型的靜態文件可能是帶有 JavaScript 函數的文件或徽標圖像。它們都放在 flaskr/static 目錄下,并用 url_for('static', filename='...') 引用。
本教程不關注如何編寫 CSS,因此您只需將以下內容復制到 flaskr/static/style.css 文件中:
html { font-family: sans-serif; background: #eee; padding: 1rem; } body { max-width: 960px; margin: 0 auto; background: white; } h1 { font-family: serif; color: #377ba8; margin: 1rem 0; } a { color: #377ba8; } hr { border: none; border-top: 1px solid lightgray; } nav { background: lightgray; display: flex; align-items: center; padding: 0 0.5rem; } nav h1 { flex: auto; margin: 0; } nav h1 a { text-decoration: none; padding: 0.25rem 0.5rem; } nav ul { display: flex; list-style: none; margin: 0; padding: 0; } nav ul li a, nav ul li span, header .action { display: block; padding: 0.5rem; } .content { padding: 0 1rem 1rem; } .content > header { border-bottom: 1px solid lightgray; display: flex; align-items: flex-end; } .content > header h1 { flex: auto; margin: 1rem 0 0.25rem 0; } .flash { margin: 1em 0; padding: 1em; background: #cae6f6; border: 1px solid #377ba8; } .post > header { display: flex; align-items: flex-end; font-size: 0.85em; } .post > header > div:first-of-type { flex: auto; } .post > header h1 { font-size: 1.5em; margin-bottom: 0; } .post .about { color: slategray; font-style: italic; } .post .body { white-space: pre-line; } .content:last-child { margin-bottom: 0; } .content form { margin: 1em 0; display: flex; flex-direction: column; } .content label { font-weight: bold; margin-bottom: 0.5em; } .content input, .content textarea { margin-bottom: 1em; } .content textarea { min-height: 12em; resize: vertical; } input.danger { color: #cc2f2e; } input[type=submit] { align-self: start; min-width: 10em; }刷新一下之前的頁面,效果就是醬紫的了。
應該是比之前的好看一些了。
總結
以上是生活随笔為你收集整理的【Flask】官方教程(Tutorial)-part2:蓝图-视图、模板、静态文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: onblur属性详解
- 下一篇: 【ML】管理和跟踪机器学习实验