Flask-補(bǔ)充WTForms表單驗(yàn)證
Flask-WTF
Flask-WTF是簡(jiǎn)化了WTForms操作的一個(gè)第三方庫(kù)昆著。WTForms表的二個(gè)主要功能是驗(yàn)證用戶提交數(shù)據(jù)的合法性以及渲染模板惧辈。當(dāng)然還包括一些其他功能:CSRF保護(hù)封锉,文件上傳等莽使。安裝Flask-WTF默認(rèn)也會(huì)安裝WTForms锐极,因此使用以下命令安裝Flask-WTF
1?pip install flask-wtf
表單驗(yàn)證
安裝完Flask-WTF后,來(lái)看下第一個(gè)功能芳肌,就是用表單來(lái)做數(shù)據(jù)驗(yàn)證灵再,現(xiàn)在有一個(gè)forms.py文件,然后再里面創(chuàng)建一個(gè)RegistForm的注冊(cè)驗(yàn)證表單
1 class RegistForm(Form):
2? ? ? ? name = StringField(validators=[length(min=4,max=25)])
3? ? ? ? email = StringField(validators=[email()])
4? ? ? ? password = StringField(validators=[DataRequired(),length(min=6,max=10),EqualTo('confirm')])
5? ? ? ? confirm = StringField()
在這個(gè)表單里面指定需要上傳的參數(shù)亿笤,且指定了驗(yàn)證器翎迁,比如name的長(zhǎng)度應(yīng)該在4-25之間。email必須要滿足郵箱的格式净薛。password長(zhǎng)度必須在6-10之間汪榔,并且應(yīng)該和confirm相等才能通過(guò)驗(yàn)證
寫完表單后,接下來(lái)就是regist.html文件
1 <form action="/regist/" method="POST">
2? ? ? ? <table>
3? ? ? ? ? ? ? <tr>
4? ? ? ? ? ? ? ? ? <td>用戶名:</td>
5? ? ? ? ? ? ? ? ? <td><input type="text" name="name"></td>
6? ? ? ? ? ? ? </tr>
7? ? ? ? ? ? ? <tr>
8? ? ? ? ? ? ? ? ? <td>郵箱:</td>
9? ? ? ? ? ? ? ? ? <td><input type="email" name="email"></td>
10? ? ? ? ? ? </tr>
11? ? ? ? ? ? <tr>
12? ? ? ? ? ? ? ? ? <td>密碼:</td>
13? ? ? ? ? ? ? ? ? <td><input type="password" name="password"></td>
14? ? ? ? ? ? </tr>
15? ? ? ? ? ? <tr>?
16? ? ? ? ? ? ? ? <td>確認(rèn)密碼:</td>
17? ? ? ? ? ? ? ? <td><input type="password" name="confirm"></td>
18? ? ? ? ? ? </tr>
19? ? ? ? ? ? <tr>
20? ? ? ? ? ? ? ? ?<td></td>
21? ? ? ? ? ? ? ? <td><input type="submit" value="提交"></td>
22? ? ? ? ? ? </tr>
23? ? ? ? </table>
24 </form>
再來(lái)看視圖函數(shù)regist
1 @app.route('/regist/',methods=['POST','GET'])
2 def regist():
3? ? ? ? form = RegistForm(request.form)
4? ? ? ? if request.method == 'POST' and form.validate():
5? ? ? ? ? ? ? user = User(name=form.name.data,email=form.email.data,password=form.password.data)
6? ? ? ? ? ? ? db.session.add(user)
7? ? ? ? ? ? ? db.session.commit()
8? ? ? ? ? ? ? return '注冊(cè)成功!'
9? ? ? ? return render_template('regist.html')
RegistForm傳遞的是request.form進(jìn)去進(jìn)行初始化肃拜,并且判斷form.validate會(huì)返回用戶提交的數(shù)據(jù)是否滿足表單的驗(yàn)證
渲染模板
form還可以渲染模板痴腌,讓你少寫了一些代碼,比如重寫以上例子燃领,RegistForm表單代碼如下
1 class RegistForm(Form):
2? ? ? ? name = StringField('用戶名:',validators=[length(min=4,max=25)])
3? ? ? ? email = StringField('郵箱:'validators=[email()])
4? ? ? ? password = StringField('密碼:',validators=[DataRequired(),length(min=6,max=10),EqualTo('confirm')])
5? ? ? ? confirm = StringField('確認(rèn)密碼:')
以上增強(qiáng)了第一個(gè)位置參數(shù)士聪,用來(lái)在html文件中,做標(biāo)簽提示作用猛蔽。
在app中的視圖函數(shù)中剥悟,修改為如下
@app.route('/regist/',methods=['POST','GET'])
def regist():
? ? form = RegistForm(request.form)
? ? if request.method == 'POST' and form.validate():
? ? ? ? user = User(name=form.name.data,email=form.email.data,password=form.password.data)
? ? ? ? db.session.add(user)
? ? ? ? db.session.commit()
? ? ? ? return '注冊(cè)成功!'
? ? return render_template('regist.html',form=form)
以上唯一的不同是在渲染模板是傳入了form表單參數(shù)進(jìn)去,這樣在模板中就可以使用表單form變量了。
就下來(lái)看下regist.html文件
<form action="/regist/" method="POST">
? ? <table>
? ? ? ? <tr>
? ? ? ? ? ? <td>{{ form.name.label }}</td>
? ? ? ? ? ? <td>{{ form.name() }}</td>
? ? ? ? </tr>
? ? ? ? <tr>
? ? ? ? ? ? <td>{{ form.email.label }}</td>
? ? ? ? ? ? <td>{{ form.email() }}</td>
? ? ? ? </tr>
? ? ? ? <tr>
? ? ? ? ? ? <td>{{ form.password.label }}</td>
? ? ? ? ? ? <td>{{ form.password() }}</td>
? ? ? ? </tr>
? ? ? ? <tr>
? ? ? ? ? ? <td>{{ form.confirm.label }}</td>
? ? ? ? ? ? <td>{{ form.confirm() }}</td>
? ? ? ? </tr>
? ? ? ? <tr>
? ? ? ? ? ? <td></td>
? ? ? ? ? ? <td><input type="submit" value="提交"></td>
? ? ? ? </tr>
? ? </table>
</form>
文件上傳
1.在模板中懦胞,form表單中替久,需要指定encotype='multipart/form-data'才能上傳文件。
2.在后臺(tái)如想要獲取上傳文件躏尉,那么應(yīng)該使用request.files.get(’avatar')來(lái)獲取蚯根。
3.保存文件之前,先要使用werkzeug.utils.secure_filename來(lái)對(duì)上傳上來(lái)的文件名進(jìn)行一個(gè)過(guò)濾胀糜。這樣才能保證不會(huì)有安全問(wèn)題颅拦。
4.獲取到上傳上來(lái)的文件后,使用avatar.save(路徑)方法來(lái)保存文件教藻。
5.從服務(wù)器上讀取文件后距帅,應(yīng)該定義一個(gè)url與視圖函數(shù),來(lái)獲取指定的文件括堤。在這個(gè)視圖函數(shù)中碌秸,使用send_from_directory(文件的目錄,文件名)來(lái)獲取悄窃。
@app.route('/upload/',methods=['GET','POST'])
def upload():
? ? if request.method == 'GET':
? ? ? ? return render_template('upload.html')
? ? else:
? ? ? ? # 獲取描述信息
? ? ? ? desc = request.form.get("desc")
? ? ? ? avatar = request.files.get("avatar")
? ? ? ? filename = secure_filename(avatar.filename)
? ? ? ? avatar.save(os.path.join(UPLOAD_PATH,filename))
? ? ? ? print(desc)
? ? ? ? return '文件上傳成功'
@app.route('/images/<filename>/')
def get_image(filename):
? ? return send_from_directory(UPLOAD_PATH,filename)
對(duì)上傳文件使用表單驗(yàn)證:
1.定義表單時(shí)讥电,對(duì)文件的字段,需要采用FileField這個(gè)類型轧抗。
2.驗(yàn)證器應(yīng)該從flask_wtf.file中導(dǎo)入恩敌。flask_wtf.file.FileRequired是用來(lái)驗(yàn)證文件上傳是否為空。flask_wtf.file.FileAllowed用來(lái)驗(yàn)證上傳的文件的后綴名横媚。
3.在視圖文件中纠炮,使用from werkzeug.datastructures import CombinedMultiDict來(lái)把request.form與request.files 來(lái)進(jìn)行合并。再傳給表單來(lái)驗(yàn)證灯蝴。
1 from werkzeug.datastructures import CombinedMultiDict
2 form = UploadForm(CombinedMultiDict([request.form,request.files]))
cookie和session
1.cookie:在網(wǎng)站中恢口,http請(qǐng)求是無(wú)狀態(tài)的。即使第一次和服務(wù)器連接后且登錄成功后穷躁,第二次請(qǐng)求服務(wù)器依然不能知道當(dāng)前請(qǐng)求是那個(gè)用戶弧蝇。cookie的出現(xiàn)就是為了解決此問(wèn)題,第一次登錄后服務(wù)器返回?cái)?shù)據(jù)(cookie)給瀏覽器折砸,然后瀏覽器保存在本地,當(dāng)該用戶發(fā)送第二次請(qǐng)求時(shí)沙峻,就會(huì)自動(dòng)把上次請(qǐng)求存貯的cookie數(shù)據(jù)自動(dòng)的攜帶給服務(wù)器睦授,服務(wù)器通過(guò)瀏覽器攜帶的數(shù)據(jù)就能判斷當(dāng)前用戶是那個(gè)了。cookie存貯的數(shù)據(jù)量有限摔寨,不同的瀏覽器有不同的儲(chǔ)存大小去枷,但一般不超過(guò)4KB。因此使用cookie只能儲(chǔ)存一些小量的數(shù)據(jù)。
2.session:session和cookie的作用有點(diǎn)類似删顶,都是為了儲(chǔ)存用戶相關(guān)的信息竖螃。不同的是,cookie是儲(chǔ)存在本地瀏覽器逗余,session是一個(gè)思路特咆、一個(gè)概念、一個(gè)服務(wù)器儲(chǔ)存授權(quán)信息的解決方案录粱,不同的框架腻格,不同的語(yǔ)言有不同的實(shí)現(xiàn)。雖然實(shí)現(xiàn)不一樣啥繁,但他們的目的都是服務(wù)器為了方便存儲(chǔ)數(shù)據(jù)的菜职。session的出現(xiàn),是為了解決cookie存儲(chǔ)不安全的問(wèn)題的旗闽。
3.cookie和session結(jié)合使用:web開(kāi)發(fā)發(fā)展至今酬核,cookie和session的使用已經(jīng)出現(xiàn)了一次非常成熟的方案。在如今的市場(chǎng)或者企業(yè)里适室,一般有二種存儲(chǔ)方式:
嫡意。存儲(chǔ)服務(wù)端:通過(guò)cookie存儲(chǔ)一個(gè)session_id,然后具體的數(shù)據(jù)是保存在session中。如果用戶已經(jīng)登錄亭病,則服務(wù)器會(huì)在cookie中保存一個(gè)session_id鹅很,下次再請(qǐng)求時(shí),會(huì)把該session_id攜帶上來(lái)罪帖,服務(wù)器根據(jù)session_id在session庫(kù)中獲取用戶的session數(shù)據(jù)促煮。就能知道該用戶到底是誰(shuí),以及之前保存的一些狀態(tài)信息整袁。這種專業(yè)術(shù)語(yǔ)叫server side session菠齿。存儲(chǔ)在服務(wù)器的數(shù)據(jù)會(huì)更加的安全,不容易被竊取坐昙。但存儲(chǔ)在服務(wù)器也有一定的弊端绳匀,會(huì)占用服務(wù)器色資源,但現(xiàn)在服務(wù)器已經(jīng)發(fā)展至今炸客,一些session信息還是卓卓有余的疾棵。
。將session數(shù)據(jù)加密痹仙,然后存儲(chǔ)在cookie中是尔。這種專業(yè)術(shù)語(yǔ)叫client side session。flask采用的就是這種方式开仰,但也可以替換成其他形式拟枚。
flask中使用cookie和session
1.cookie:在Flask中操作cookie薪铜,是通過(guò)response對(duì)象來(lái)操作,可以在response返回之前恩溅,通過(guò)response.set_cookie來(lái)設(shè)置隔箍,這個(gè)方法有一下幾個(gè)參數(shù)需要注意:
key:設(shè)置的cookie的key。
value:key對(duì)應(yīng)的value脚乡。
max_age:改cookie的過(guò)期時(shí)間蜒滩,如果不設(shè)置,則瀏覽器關(guān)閉后就會(huì)自動(dòng)過(guò)期每窖。
expires:過(guò)期時(shí)間帮掉,應(yīng)該是一個(gè)datetime類型
domain:該cookie在那個(gè)域名中有效。一般設(shè)置子域名窒典,比如cms.example.com蟆炊。
path:該cookie在哪個(gè)路徑下有效。
2.session:Flask中的session是通過(guò)from flask import session瀑志。然后添加值key和value進(jìn)去既可涩搓。并且,F(xiàn)lask中的session機(jī)制是將session信息加密劈猪,然后存儲(chǔ)在cookie中昧甘。專業(yè)術(shù)語(yǔ)叫client side session。