Flask-WTF擴展可以把處理Web表單的過程變成一種愉悅的體驗。
安裝命令
pip install flask-wtf
跨站請求偽造保護
Flask-WTF能保護所有表單免受跨站請求偽造(CSRF)的攻擊。惡意網(wǎng)站把請求發(fā)送到被攻擊者已登錄的其他網(wǎng)站時就會引發(fā)CSRF攻擊。
為了實現(xiàn)CSRF,F(xiàn)lask-WTF需要程序設(shè)置一個密鑰。Flask-WTF使用這個密鑰生成加密令牌,再用令牌驗證請求中表單數(shù)據(jù)的真?zhèn)巍?/p>
設(shè)置Flask-WTF
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
app.config字典可用來存儲框架、擴展和程序本身的配置變量。使用標準的字典句法就能把配置值添加到app.config對象中。
表單類
使用Flask-WTF時,每個Web表單都由一個繼承自Form的類表示。這個類定義表單中的一組字段,每個字段都用對象表示。字段對象可附屬一個或多個驗證函數(shù)。驗證函數(shù)用來驗證用戶提交的輸入值是否符合標準。
定義表單類
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
class NameForm(Form):
name = StringField('What is your name?', validators=[DataRequired()])
submit = SubmitField('Submit')
字段構(gòu)造函數(shù)的第一個參數(shù)是把表單渲染成HTML時使用的標號。
StringField構(gòu)造函數(shù)中的可選參數(shù)validators指定一個由驗證函數(shù)組成的列表,在接受用戶提交的數(shù)據(jù)之前驗證數(shù)據(jù)。驗證函數(shù)DataRequired()確保提交的字段不為空。
WTForms支持的HTML標準字段
| 字段類型 | 說明 |
|---|---|
| StringField | 文本字段 |
| TextAreaField | 多行文本字段 |
| PasswordField | 密碼文本字段 |
| HiddenField | 隱藏文本字段 |
| DateField | 文本字段,值為datetime.date格式 |
| DatetimeField | 文本字段,值為datetime.datetime格式 |
| IntegerField | 文本字段,值為整數(shù) |
| DecimalField | 文本字段,值為decimal.Decimal |
| FloatField | 文本字段,值為浮點數(shù) |
| BooleanField | 復(fù)選框,值為True和False |
| RadioField | 一組單選框 |
| SelectField | 下拉列表 |
| SelectMultipleField | 下拉列表,可選擇多個值 |
| FileField | 文件上傳字段 |
| SubmitField | 表單提交按鈕 |
| FormField | 把表單作為字段嵌入另一個表單 |
| FieldList | 一組指定的類型的字段 |
WTForms驗證函數(shù)
| 驗證函數(shù) | 說明 |
|---|---|
| 驗證電子郵件地址 | |
| EqualTo | 比較兩個字段的值;常用于要求輸入兩次密碼進行確認的情況 |
| IPAddress | 驗證IPv4網(wǎng)絡(luò)地址 |
| Length | 驗證輸入字符串的長度 |
| NumberRange | 驗證輸入的值在數(shù)字范圍內(nèi) |
| Optional | 無輸入時跳過其他驗證函數(shù) |
| Required | 確保字段中有數(shù)據(jù) |
| Regexp | 使用正則表達式驗證輸入值 |
| URL | 驗證URL |
| AnyOf | 確保輸入值在可選值列表中 |
| NoneOf | 確保輸入值不在可選值列表中 |
把表單渲染成HTML
表單字段是可調(diào)用的,在模板中調(diào)用后會渲染成HTML。
import指令允許導(dǎo)入模板中的元素并用在多個模板中。導(dǎo)入的bootstrap/wtf.html文件中定義了一個使用Bootstrap渲染Flask-WTF表單對象的輔助函數(shù)。wtf.quick_from()函數(shù)的參數(shù)為Flask-WTF表單對象,使用Bootstrap的默認樣式渲染傳入的表單。
使用Flask-WTF和Flask-Bootstrap渲染表單
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content%}
<div class="page-header">
<h1>Hello,{% if name %}{{name}}{% else %}Stranger{% endif %}!</h1>
</div>
{{wtf.quick_form(form)}}
{% endblock %}
Jinja2中的條件語句格式為{% if condition %}...{% else %}...{% elseif %}。
在視圖函數(shù)中處理表單
路由方法
@app.route('/', methods=['GET', 'POST'])
def index():
name = None
form = NameForm()
if form.validate_on_submit():
name = form.name.data
form.name.data = ''
return render_template('index.html', form=form, name=name)
app.route修飾器中添加的methods參數(shù)告訴Flask在URL映射中把這個視圖函數(shù)注冊為GET和POST請求的處理過程。如果沒指定methods參數(shù),就只把視圖函數(shù)注冊為GET請求的處理程序。
重定向和用戶會話
重定向是一種特殊的相應(yīng),響應(yīng)內(nèi)容是URL,而不是包含HTML代碼的字符串。這個技巧稱為 POST/重定向/GET模式。
用戶對話是一種私有存儲,存在于每個連接到服務(wù)器的客戶端中。用戶會話是請求上下文中的變量,名為session。
默認情況下,用戶會話保存在客戶端cookie中,使用設(shè)置的SECRET_KEY進行加密簽名。如果篡改了cookie中的內(nèi)容,簽名就會失效,會話也會隨之失效。
重定向和用戶會話
from flask import Flask, render_template, session, redirect, url_for
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('index.html', form=form, name=session.get('name'))
redirect()是個輔助函數(shù),用來生成HTTP重定向響應(yīng)。redirect()函數(shù)的參數(shù)是重定向的URL,這里使用的重定向URL是程序的跟地址。
url_for()函數(shù)使用URL映射生成URL,從而保證URL和定義的路由兼容,而且修改路由名字后依然可用。
url_for()函數(shù)的第一個且唯一必須指定的參數(shù)是端點名,即路由的內(nèi)部名字。路由的端點是相應(yīng)視圖函數(shù)的名字。
使用get()獲取字典中鍵對應(yīng)的值以避免未找到鍵的異常情況,因為對于不存在的值,get()會返回默認值None。
Flash消息
Flash消息
from flask import Flask, render_template, session, redirect, url_for, flash
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
old_name = session.get('name')
if old_name is not None and old_name != form.name.data:
flash('Looks like you have changed your name!')
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('index.html', form=form, name=session.get('name'))
僅調(diào)用flash()函數(shù)并不能把消息顯示出來,程序使用的模板要渲染這些消息。最好在基模板中渲染Flash消息,因為這樣所有頁面都能使用這些消息。Flask把get_flashed_messages()函數(shù)開放給模板,用來獲取并渲染消息。
渲染Flash消息
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message}}
</div>
{% endfor %}
{% block page_content %}{% endblock %}
在模板中使用循環(huán)是因為之前的請求循環(huán)中每次調(diào)用flash()函數(shù)時都會生成一個消息,所以可能有多個消息在排隊等待顯示。get_flashed_messages()函數(shù)獲取的消息在下次調(diào)用時不會再次返回,因此Flash消息只顯示一次,然后就消失了。