# Flask實作_ext_03_Flask-WTF_表單建立 ###### tags: `flask` `flask_ext` `python` `wtform` ## 說明 :::danger 官方文件: * [Flask-wtf](https://flask-wtf.readthedocs.io/en/stable/) ::: 表單類元件在網頁設計中是不可或缺的一個元素,雖然可以透過`request.form`來取得前端的表單,但不免俗的還是要自己硬刻一堆重覆性質的Html。 `Flask-wtf`除了可以減化這類工作之外,它還預設有避免`CSRF`攻擊的功能,這對資訊安全上有絕對的幫助。 ## 安裝 ```python= pip install flask-wtf ``` 透過`pip`安裝`flask-wtf`會自動將相依套件`WTForms`下載安裝,實作上必需要設置Flask參數`SECRET_KEY`,否則會報錯,如下設置: ```python from flask import Flask app = Flask(__name__) app.config('SECRET_KEY') = 'your key values' ``` ## 範例 `Flask-wtf`是一個類別函數,一切都從繼承`FlaskForm`開始,再各別從`wtforms`去引入需求的欄位類別以及驗證。 建立一個新的Python文件,檔案名稱為`view_form.py`,相關作業說明如註解,如下: ```python= # 引入flask_wtf from flask_wtf import FlaskForm # 各別引入需求欄位類別 from wtforms import StringField, SubmitField from wtforms.fields.html5 import EmailField # 引入驗證 from wtforms.validators import DataRequired, Email # 從繼承FlaskForm開始 class UserForm(FlaskForm): username = StringField('UserName', validators=[DataRequired(message='Not Null')]) email = EmailField('Email', validators=[DataRequired(message='Not Null')]) submit = SubmitField('Submit') ``` 第11行:建置一個字串欄位,並利用`validators`來設置欄位的驗證,驗證項目為`DataRequired`,代表必填,最後是當驗證失敗的時候就出現驗證失敗的訊息`message` 現在讓我們加入一個啟動Flask的Python文件,命名為`flask_start.py`,如下: ```python= from flask import Flask, render_template # 引入form類別 from view_form import UserForm app = Flask(__name__) @app.route('/user', methods=['GET', 'POST']) def user(): form = UserForm() # flask_wtf類中提供判斷是否表單提交過來的method,不需要自行利用request.method來做判斷 if form.validate_on_submit(): return 'Success Submit' # 如果不是提交過來的表單,就是GET,這時候就回傳user.html網頁 return render_template('user.html', form=form) if __name__ == '__main__': app.debug = True app.run() ``` 第14行:回傳渲染網頁`user.html`,並傳遞了一個表單物件參數`form`,這讓我們可以在Html文件中可以利用`jinja2`的語法來操作它 部份註解寫在程式碼上,其中`form.validate_on_submit()`是由`flask_wtf`提供的method,可以直接確認`POST`與欄位驗證以及最重要的`CSRF`。 最後在`templates`內加入Html文件,命名為`user.html` ```htmlmixed= <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="POST" action="{{ url_for('user') }}" > {{ form.hidden_tag() }} <p>{{ form.username.label }} {{ form.username }} {% if form.username.errors %} {% for error in form.username.errors %} {{ error }} {% endfor %} {% endif %} </p> <p>{{ form.email.label }} {{ form.email }}</p> <p>{{ form.submit.label }} {{ form.submit }}</p> </form> </body> </html> ``` 第9行:這是必填的一個項目,沒有填會出現異常 第10行:`{{ form.username.label }}{{ form.username }}`,`form`是由後端拋到前端來的一個參數,`username`是我們在Form中所設置的屬性名稱,而`label`是該屬性的第一個參數。 一切就緒,這時候執行專案會遇到錯誤訊息如下: ![](https://i.imgur.com/epheNZL.png) 這是因為`flask_wtf`預設需要設置密碼,也是為了避免一開始所說的`CSRF`攻擊。 在`flask_start.py`上,加上`app.config['SECRET_KEY']`的參數設置,如下: ```python= ...上略 if __name__ == '__main__': app.debug = True app.config['SECRET_KEY']='your key' app.run() ``` 重新執行專案,成功的渲染版面,如下圖: ![](https://i.imgur.com/Q28xf9f.png) 查看原始檔,轉譯的非常完美,如下圖: ![](https://i.imgur.com/c01aRbm.png) 故意讓email格式輸入錯誤,如下圖: ![](https://i.imgur.com/7KchZJs.png) 這錯誤訊息是因為欄位本身設置為email欄位,這是`html5`預設的卡控機制。 故意讓username是空值,異常訊息如下圖: ![](https://i.imgur.com/fnr0408.png) 透過`field.errors`來取得錯誤訊息之後呈現! ## 總結 利用一個簡單的範例來認識`flask-wtf`,透過參數的傳遞將物件由後端拋到前端,再搭配`jinja2`語法來操作,簡化的程度大到令人感到不可思議,原來開發一個表單可以這麼快速,但這還不足以驚豔,因為下一話我們會搭配`flask-bootstrap`來更快速的開發表單,下面有其它的部份,是簡單的列出`WTForms`支援的欄位清單以及驗證函數,更詳細的當然是請您參閱官方文件說明。 **Flask-WTF_表單建立_進階:**[Flask實作_ext_04_Flask-WTF_表單建立_進階](https://hackmd.io/s/r1pPM7ZXz) ## 其它 ### WTForms支援欄位清單 |類型|說明| |-----|-----| |StringField|字串欄位| |TextAreaField|多行字串欄位| |PasswordField|密碼欄位(出現\*)| |HiddenField|隱藏欄位| |DateField|日期欄位datetime.date| |DateTimeField|日期時間欄位datetime.datetime| |IntegerField|整數欄位| |DecimalField|精度數值欄位decimal.Decimal| |FloatField|浮點數欄位| |BooleanField|布林欄位| |RadioField|單選radio| |SelectField|下拉表單| |SelectMultipleField|下拉表單(可多選)| |FileField|文件上傳欄位| |SubmitField|提交表單按鈕| |FormField|Form中有Form| |FieldList|?| ### WTForms驗證函數 |函數|說明| |-----|-----| |Email|驗證電子郵件欄位| |EqualTo|比較兩欄位數值是否相同| |IPAddress|驗證ip位址| |Length|驗證字串長度| |NumberRange|驗證數值區間| |Optional|?| |DataRequired|必填欄位| |Regexp|以正則式驗證| |URL|驗證網址| |AnyOf|驗證輸入值在列表中| |NoneOf|驗證輸入值不在列表中|