《Flasky Web開發四》Web表單

request.form能獲取POST請求中提交的表單數據,但是爲了某些重複操作,例如生成表單的HTML代碼和驗證提交的表單數據,使用Flask-WTF擴展。

       pip install flask-wtf

4.1 跨站請求僞造保護

默認下,Flask-WTF能保護所有表單免受跨站請求僞造(Cross-Site Request Forgery, CSRF)攻擊。惡意網站把請求發送到被攻擊者已登錄的其他網站使就會引發CSRF攻擊。

爲了實現CSRF保護,Flask-WTF需要程序設置一個密鑰,使用密鑰生成加密令牌,再用令牌驗證請求中表達數據的真僞。

app = Flask(__name__)

app.config['SECRET_KEY'] = 'hard to guess string'

此處app.config字典可以用來存儲框架、擴展和程序本身的配置變量。SECRET_KEY配置變量是通用密鑰,可在Flask和多個第三方擴展中使用。注意:爲了安全性,密鑰不要直接寫入代碼,而是存在環境變量。

4.2 表單類

       使用Flask-WTF時,每個Web表單都由一個繼承自Form的類表示。這個類定義表單中的一組字段,每個字段都用對象表示。字段對象可附屬一個或多個驗證函數。驗證函數用來驗證用戶提交的輸入值是否符合要求。

       from flask_wtf import Form  //flask.ext.wtf轉換成flask_wtf

from wtforms import StringField, SubmitField

from wtforms.validators import Required

 

class NameForm(Form):

        name = StringField('Whst is your name?', validators = [Required()])

        submit = SubmitField('Submit')

       此處代碼爲一個簡單的Web表單,包含一個文本字段和一個提交按鈕。NameForm表單中有一個名爲name的文本字段和一個名爲submit的提交按鈕。StringField類表示屬性爲type=”text”的<input>元素,SubmitField類表示屬性爲type=”submit”的<input>元素。StringField構造函數的可選參數validators指定一個由驗證函數組成的列表,在接受用戶提交的數據之前驗證數據。驗證函數Required()確保提交字段不爲空。

       Form基類由Flask-WTF擴展定義,所以從flask_wtf導入。字段和驗證函數直接從WTForms包中導入。

       WTForms支持的HTML標準字段和驗證函數自行查閱。

4.3 把表單渲染成HTML

       視圖函數把一個NameForm實例通過參數form傳入模板,在模板中生成表單

       <form method=”POST”>

              {{ form.hidden_tag() }}

              {{ form.name.label }} {{ form.name() }}

              {{ form.submit() }}

       </form>

       可以爲字段制定id或class屬性,然後定義CSS樣式

              {{ form.name.label }} {{ form.name(id=’my-text-field’) }}

       這種方式渲染表單的工作量較大,最好使用Bootstrap的表單樣式

       {% import “boostrap/wtf.html” as wtf %}

       {{ wtf.quick_form(form) }}

4.4 在視圖函數中處理表單

       GET - 從指定的資源請求數據

POST - 向指定的資源提交要被處理的數據

       @app.route('/', method=['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)

       此處將表單提交作爲POST請求處理更加便利。如果GET請求提交表單,由於GET請求沒有主體,數據會以查詢字符串的形式附加到URL中。

       用戶第一次訪問程序時,服務器收到一個沒有表單數據的GET請求,所以if語句部分跳過,通過渲染模板處理請求,並傳入表單對象和值爲None的name變量作爲參數,用戶在瀏覽器上看到一個表單。

      用戶提交表單後,服務器收到一個包含數據的POST請求。validate_on_submit()調用name字段上附屬的Required()驗證函數,如果名字不爲空則驗證通過,validate_on_submit()返回True。if語句中,name獲得名字,data屬性設爲空字符串從而清空表單字段。最後調用render_template()函數渲染模板,參數name的值爲表單中輸入的名字。

hello.py

       from flask import Flask, render_template

from flask_bootstrap import Bootstrap

from flask_moment import Moment

from flask_wtf import FlaskForm

from wtforms import StringField, SubmitField

from wtforms.validators import DataRequired

 

app = Flask(__name__)

app.config['SECRET_KEY'] = 'hard to guess string'

 

bootstrap = Bootstrap(app)

moment = Moment(app)

 

class NameForm(FlaskForm):

    name = StringField('What is your name?', validators=[DataRequired()])

    submit = SubmitField('Submit')

 

@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.run(host='0.0.0.0',debug=True)

index.html

       {% 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 %}

4.5 重定向和用戶會話

       可用性問題:用戶輸入名字後提交表單,點擊瀏覽器刷新按鈕,會有再次提交表單警告。這是因爲刷新頁面時,瀏覽器會重新發送之前已經發送過的最後一個請求,而如果這個請求包含表單數據的POST請求,刷新頁面會再次提交表單。

       解決辦法:POST/重定向/GET模式

       使用重定向作爲POST請求的響應,響應內容爲URL而不是常規的包含HTML代碼的字符串。瀏覽器收到重定向響應,會向重定向的URL發起GET請求,顯示頁面內容。而POST請求的form.name.data獲取的輸入名字將存儲在用戶會話中。

用戶會話是一種私有存儲,存在每個連接到服務器的客戶端中,默認存在客戶端的cookie中。

       from flask import Flask, render_template, session, redirect, url_for

from flask_bootstrap import Bootstrap

from flask_moment import Moment

from flask_wtf import FlaskForm

from wtforms import StringField, SubmitField

from wtforms.validators import DataRequired

 

app = Flask(__name__)

app.config['SECRET_KEY'] = 'hard to guess string'

 

bootstrap = Bootstrap(app)

moment = Moment(app)

 

class NameForm(FlaskForm):

    name = StringField('What is your name?', validators=[DataRequired()])

    submit = SubmitField('Submit')

 

@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'))

       此處代碼,原本的局部變量name現在保存在用戶會話session[‘name’]中,所以兩次請求之間能記住輸入的值。合法表單數據的請求調用redirect()函數,參數爲重定向的地址。url_for()默認唯一必須參數是路由的內部名字,默認爲視圖函數的名字,這裏指index()。render_template()函數使用session.get(‘name’)直接從會話中讀取name參數。get()獲取字典中鍵對應的值,若無返回none。

4.6 Flask消息

       請求完成後,響應消息提示用戶輸入是否正確。使用flash()函數,在發給客戶端的下一個響應中顯示一個消息。

hello.py

       @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'))

       每次提交的名字都會和存儲在用戶會話中的名字作比較,如果兩個名字不同調用flash()函數,在發給客戶端的下一個響應中顯示一個消息。

base.html(建議在基模板中渲染)

       {% block content %}

<div class="container">

    {% for message in get_flashed_messages() %}

    <div class="alert alert-warning">

        <button type="button" class="close" data-dismiss="alert">&times;</button>

        {{ message }}

    </div>

    {% endfor %}

 

    {% block page_content %}{% endblock %}

</div>

{% endblock %}

       此處代碼使用了for循環,因爲在之前的請求循環中,每次調用flash()都會生成一個消息,所以可能有多個消息在排隊等待顯示。get_flashed_messages()函數獲取的消息在下次調用時不會再次返回,因此Flash消息只顯示一次。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章