處理表單數據
表單數據的處理涉及很多內容,從獲取數據到保存數據大致有以下步驟:
1、 解析請求,獲取表單數據
2、 對數據進行必要的轉換,比如講勾選框的值轉換成python的布爾值
3、 驗證數據是否符合要求,同時驗證CSRF令牌。
4、 如果驗證未通過則需要生成錯誤消息,並在模板中顯示錯誤消息。
5、 如果驗證通過,就把數據保存到數據庫或做進一步處理
使用Flask-WTF和WTForms可以極大地簡化這些步驟
提交表單
在HTML中,當<form>標簽聲明的表單中類型為submit的提交字段被單擊時,就會創建一個表單的HTTP請求,請求中包含表單各個字段的數據。表單的提交行為主要有三個屬性控制,如下表:
form標簽的action屬性用來指定表單被提交的目標URL,默認為當前URL,就是渲染該模板的路由所在的URL。
當使用get方法提交表單時,表單的數據會以查詢字符串的形式附加在請求的URL里,如:
127.0.0.1:5000:/basic?username=xiaxiaoxu&password=12345
GET方式僅適用於長度不超過3000個字符,且不包含敏感信息的表單。因為這種方式會直接將用戶提交的表單數據暴露在URL中,容易被攻擊者截獲,實例中的情況是危險的。因此處處於安全的考慮,我們一般使用post方法提交表單。使用post方式時,按照默認的編碼類型,表單數據會存儲在請求主體中,比如:
POST /basic HTTP/1.0
…
Content-Type:application/x-www-form-urlencoded
Content-Length:30
username=xiaxiaoxu&12345
Flask為路由設置默認的監聽方法是GET,為了支持接收表單提交發送的POST請求,我們需要在app.route()裝飾器里使用methods關鍵字為路由指定HTTP方法,比如:
@app.route('/',methods=['GET','POST']) def basic(): form=LoginForm() return render_template('basic.html',form=form)
驗證表單數據
客戶端驗證和服務器端驗證
表單的驗證通常分為以下幾種形式
客戶端驗證
客戶端驗證是指在客戶端(比如web瀏覽器)對用戶的輸入值進行驗證。比如,使用HTML5內置的驗證屬性即可實現基本的客戶端驗證(type、required、min、max、accept等)。
例如,給username字段添加required標志:
<input type=”text” name=”username” required>
如果用戶沒有輸入內容而按下提交按鈕,會彈出瀏覽器內置的錯誤提示
和其他附件HTML屬性相同,我們可以在定義表單時通過render_kw傳入這些屬性,或是在渲染表單時傳入,像requiredd這類布爾值屬性,值可以為空或是任意ASCII字符,如:
{{ form.username(required=’’) }}
除了使用HTML5提供的屬性實現基本的客戶端驗證,我們通常會使用javaScript實現完善的驗證機制,比如使用javaScript表單驗證庫-jQuery
客戶端方式可以實時動態提示用戶輸入是否正確,只有用戶輸入正確后才會將表單數據發送到服務器,客戶端驗證可以增強用戶體驗,降低服務器負載。
服務器端驗證
服務器端驗證是指用戶把輸入的數據提交到服務器端,在服務器端對數據進行驗證。如果驗證出錯就會在響應中加入錯誤信息。用戶修改后再提交表單,知道通過驗證。在flask中使用WTForms實現的就是服務器端驗證
WTForms驗證機制
WTForms驗證表單字段的方式是在實例化表單類時傳入表單數據,然后對表單實例調用validate()方法。這會逐個對字段調用字段實例化時定義的驗證器,返回表示驗證結果的布爾值。如果驗證失敗,就把錯誤消息存儲到表單實例的errors屬性對應的字典中,驗證的過程如下所示:
>>> from app import app >>> from flask import request >>> app.test_request_context('/basic').push()#激活請求上下文 >>> from flask import current_app >>> app.app_context().push()#激活程序上下文 >>> current_app.name 'app' >>>#定義LoginForm類 >>> from wtforms import Form, StringField,PasswordField,BooleanField >>> from wtforms.validators import DataRequired,length >>> class LoginForm(Form): ... username = StringField('Username', validators=[DataRequired()]) ... password = PasswordField('Password',validators=[DataRequired(),length(8,128)]) ... >>> form = LoginForm(username='',password='123') >>> form.data #表單數據字典 {'username': '', 'password': '123'} >>> form.validate() False >>> form.errors {'username': [u'This field is required.'], 'password': [u'Field must be between 8 and 128 characters long.']} >>> form2 = LoginForm(username='xiaxiaoxu', password='123456') >>> form2.data #表單數據字典 {'username': 'xiaxiaoxu', 'password': '123456'} >>> form2.validate() False >>> form2.errors #錯誤消息字典 {'password': [u'Field must be between 8 and 128 characters long.']} >>> form3 = LoginForm(username='xiaxiaoxu', password='123456789') >>> form3.data {'username': 'xiaxiaoxu', 'password': '123456789'} >>> form3.validate() True >>> form3.errors {}
因為表單使用POST方法提交,如果單純使用WTForms,在實例化表單時需要首先把request.form傳入表單類(LoginForm(username='',password='123')),而使用Flask-WTF時,表單類繼承的FlaskForm基類默認會從request.form獲取表單數據,所以不需要手動傳入。
使用POST方法提交的表單,其數據會被Flask解析成為一個字典,可以通過請求對象的form屬性獲取(request.form);使用GET方法提交的表單的數據同樣會被解析為字典,不過要通過請求對象的args屬性獲取(request.args)。