Flask學習之路(三)之表單
本文學習來源The-Flask-Mega-Tutorial-zh ,學習如何使用Web表單,再次表達對譯者的感謝,正是因為他,才能學習到這麼好的教程。本篇僅作為自己Flask入門的記錄,想通過此來記錄程式碼和自己不懂的概念。
Flask-WTF簡介
讓我們學習之前先了解下Flask-WTF外掛,通過pip3 install Flask——WTF
,在每次安裝外掛後都建議開啟Python直譯器測試是否安裝成功,測試命令為>>>import flask_wtf
,若接下來為>>>
則表明模組匯入成功,在WTForms的介紹中顯示,WTForms可以生成表單欄位的HTML,也可以在模板中自定義它,來實現程式碼和表單的分離,而Flask-WTF
為WTForms提供了一個簡單介面。
配置應用
在文中好幾處提到了鬆耦合
這個概念,在配置應用前讓我們講講鬆耦合
,它主要是為了模組的獨立性,在維基百科的程式設計部分解釋道:耦合是指一個元件直接瞭解其他元件的程度,Flask-WTF
就是一個鬆耦合的例子。在接下來的應用配置中也會用到,先讓我們看個例子,通過app.config
來配置應用
app = Flask(__name__) app.config['SECRET_KEY'] = 'you-will-never-guess'
這樣使得配置和應用程式碼處於了一個檔案,隨著程式規模的增大,會越來越不利於維護,通過鬆耦合便可以解決這個問題。讓我們在microblog/
目錄下新建config.py
模組進行配置
import os class Config(object):#python中所有的類都繼承自object類 SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'
在這裡提供了一個os.environ是存放環境變數的一個類,通過get方法來獲得SECRET_KEY
的值,在這裡os.environ是一個字典變數。而SECRET_KEY
是用來對Flask的敏感部分進行加密。此處or
為表示式,連線第一個項用來查詢SECRET_KEY
值,第二個是hardcode字串,這樣就提高了Flask應用的安全效能。
然後我們在__init__.py
裡面進行初始化,使得Flask來讀取並使用config.py
配置檔案:
from flask import Flask from config import Config app = Flask(__name__) app.config.from_object(Config) from app import routes
在這裡有一句程式碼是app.config.from_object(Config)
,在配置中使用類和繼承我們需要呼叫from_object
,可以參考技術手冊
配置完成後,我們可以在互動環境中進行測試
>>> from microblog import app >>> app.config['SECRET_KEY']
如果沒有問題則會顯示硬編碼的值'you-will-never-guess',下面來看看使用者登入表單的實現。
使用者登入表單
Flask-WTF外掛中,支援將表單欄位定義為類屬性,為了使結構更加明瞭和維護方便,我們使用app/form.py
來實現
from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, SubmitField from wtforms.validators import DataRequired class LoginForm(FlaskForm): username = StringField('Username', validators=[DataRequired()]) password = PasswordField('Password', validators=[DataRequired()]) remember_me = BooleanField('Remember Me') submit = SubmitField('Sign In')
在這裡可以看出匯入的FlaskForm
作為LoginForm
的父類,匯入的StringField、PasswordField,分別有著引數Username、Password,後面的引數validators則表明了驗證規則,BooleanField則定義了一個Checkbox型別的,若加上default='checked'預設勾選此框,而SubmitField則建立了一個submit按鈕,匯入的DataRequired
是用來進行驗證必填項的,也就是不填User和password會產生報錯,下面來看看錶單模板。
表單模板
下面完成將表單新增進模板裡,在templates
下新建login.html
:
{% extends "base.html" %} {% block content %} <h1>Sign In</h1> <form action="" method="post" novalidate> {{ form.hidden_tag() }} <p> {{ form.username.label }}<br> {{ form.username(size=32) }} </p> <p> {{ form.password.label }}<br> {{ form.password(size=32) }} </p> <p>{{ form.remember_me() }} {{ form.remember_me.label }}</p> <p>{{ form.submit() }}</p> </form> {% endblock %}
首先指明此模板繼承於基類模板,在form中定義了三個屬性,action 指向了某個真實伺服器的臨時 URL ,置空則表示當前頁面,而在method屬性裡面指定值為POST
,而有時候我們使用的方法為GET
我們看一下POST
和GET
方法區別;
使用GET
會使得提交的內容在URL中可見,如果使用GET
方法提交表單,會使密碼等洩露,而POST
通常用於提交表單和敏感資料,更多的資訊可以參考此網站
而對於form.hidden_tag()
使得跨站請求偽造保護得到了支援。它的模板引數生成了一個隱藏欄位,其中包含一個用於保護表單免受CSRF攻擊的token
。在Flask-WTF文件中寫到:
Form 類有一個 hidden_tag 方法, 它在一個隱藏的 DIV 標籤中渲染任何隱藏的欄位
{{ form.<field_name>.label }}
,{{ form.<field_name>() }}
則是為了指明其需要在渲染時轉化為HTML元素 及其類別,下面編寫表單檢視來渲染模板
表單檢視
讓我們在app/routes.py
模組中編寫這個檢視函式:
from flask import render_template from app import app from app.forms import LoginForm #程式碼中間部分 @app.route('/login') def login(): form = LoginForm() return render_template('login.html', title='Sign In', form=form)#render_templatedi第一個引數傳入頁面,然後根據後面的引數渲染模板
然後把登入的連結新增到導航欄,就是base.html
模板中:
<div> Microblog: <a href="/index">Home</a> <a href="/login">Login</a> </div>
接下來執行一下應用試一試吧!
接收表單資料
當我們在頁面中進行提交的時候,會發現有錯誤,回顧之前我們只進行了顯示錶單的工作,接下來我們通過Flask-WTF對提交的資料進行處理和驗證。
from flask import render_template, flash, redirect #關於GET、POST屬性有遺忘的話可以檢視前面的介紹 @app.route('/login', methods=['GET', 'POST']) def login(): form = LoginForm() if form.validate_on_submit(): flash('Login requested for user {}, remember_me={}'.format( form.username.data, form.remember_me.data)) return redirect('/index') return render_template('login.html', title='Sign In', form=form)
接下來讓我們看下form.validate_on_submit()
,當我們進行提交時它主要完成了兩件事:
- 通過is_submitted()通過判斷HTTP方法來確認是否提交了表單
-
通過WTForms提供的validate()來驗證表單資料(使用我們在下面的表單類裡給每個欄位傳入的驗證函式)
在stack overflow找到了自己可理解的解釋:
validate_on_submit() is a shortcut for is_submitted() and validate().Generally speaking, it is used when a route can accept both GET and POST methods and you want to validate only on a POST request.
驗證通過後會返回True,要是沒通過則會像之前一樣返回GET
請求的渲染頁面。
flash是為了向用戶反饋,比如在使用者登入成功後顯示訊息,以使得應用對使用者更加友好易用。
下面來看看redirect()
,可以觀察到其有一個URL
引數,通過英文意思我們可以猜測,其是在驗證通過後返回/index
頁面,函式的實際功能也是這樣。
當呼叫flash()
函式後,FLASk會儲存這個訊息,為了讓其顯示在頁面上,需要將訊息渲染到基礎模板中,更新後的base.html
如下:
<html> <head> {% if title %} <title>{{ title }} - microblog</title> {% else %} <title>microblog</title> {% endif %} </head> <body> <div> Microblog: <a href="/index">Home</a> <a href="/login">Login</a> </div> <hr> {% with messages = get_flashed_messages() %} {% if messages %} <ul> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %} {% block content %}{% endblock %} </body> </html>
with
結構把get_flashed_messages()
的結果賦值給變數messages
,前者返回flash()
註冊過的訊息列表,接下來判斷並遍歷渲染訊息列表,現在再次進行測試吧。
完善欄位驗證
表單驗證是為了防止提交無效資料,若接收到無效表單,則應該重新顯示錶單,讓使用者輸入合法資料,在之前的設計中我們沒有給出足夠的錯誤反饋,現在讓我們完善這個功能。(注:其實錯誤已經生成,下面通過邏輯來渲染它們)
讓我們在login.html
中為username和password欄位新增驗證描述性錯誤訊息渲染邏輯:
{% extends "base.html" %} {% block content %} <h1>Sign In</h1> <form action="" method="post" novalidate> {{ form.hidden_tag() }} <p> {{ form.username.label }}<br> {{ form.username(size=32) }}<br> {% for error in form.username.errors %} <span style="color: red;">[{{ error }}]</span> {% endfor %} </p> <p> {{ form.password.label }}<br> {{ form.password(size=32) }}<br> {% for error in form.password.errors %} <span style="color: red;">[{{ error }}]</span> {% endfor %} </p> <p>{{ form.remember_me() }} {{ form.remember_me.label }}</p> <p>{{ form.submit() }}</p> </form> {% endblock %}
相比之前,多加了一個for
迴圈來渲染錯誤資訊,讓其用紅色字型顯示出來,登入表單已經差不多完善了,下面讓我們看看包含連結的更好方法。
生成連結
在之前我們在base.html
和routes.py
中包含了一些連結的例子:
<!--base.html中--> <div> Microblog: <a href="/index">Home</a> <a href="/login">Login</a> </div> <!--routes.py中--> return redirect('/index')
flask模組提供了url_for()
函式用於獲取函式的URL
,因此在專案中所有引用到URL字串的地方,都可以使用url_for(func, **kwargs)
來獲取函式的URL,這樣做的好處在於,可以使專案更容易維護。當某函式URL發生變更時,只需修改一處地方即可,而無須修改每一處URL引用,相比硬編碼更加方便和易於維護,讓我們使用url_for
來生成連結。
結果上述程式碼可改為如下:
<!--base.html中--> <div> Microblog: <a href="/index">Home</a> <a href="/login">Login</a> </div>
在routes.py
中的login
中也要進行修改,不過先得引入url_for模組。
from flask import render_template, flash, redirect, url_for #中間程式碼部分 @app.route('/login', methods=['GET', 'POST']) def login(): form = LoginForm() if form.validate_on_submit(): # ... return redirect(url_for('index'))#修改此句 # ...
在此表單差不多就完成了,謝謝觀看。