Flask-WTF
Flask-WTF
是簡(jiǎn)化了WTForms
操作的一個(gè)第三方庫(kù)。WTForms
表單的兩個(gè)主要功能是驗(yàn)證用戶(hù)提交數(shù)據(jù)的合法性以及渲染模板典勇。當(dāng)然還包括一些其他的功能:CSRF保護(hù)
,文件上傳等叮趴。安裝Flask-WTF
默認(rèn)也會(huì)安裝WTForms
痴柔,因此使用以下命令來(lái)安裝Flask-WTF
:
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)證表單:
class RegistForm(Form):
name = StringField(validators=[length(min=4,max=25)])
email = StringField(validators=[email()])
password = StringField(validators=[DataRequired(),length(min=6,max=10),EqualTo('confirm')])
confirm = StringField()
在這個(gè)里面指定了需要上傳的參數(shù)搔驼,并且指定了驗(yàn)證器谈火,比如name
的長(zhǎng)度應(yīng)該在4-25
之間。email
必須要滿(mǎn)足郵箱的格式舌涨。password
長(zhǎng)度必須在6-10
之間糯耍,并且應(yīng)該和confirm
相等才能通過(guò)驗(yàn)證。
寫(xiě)完表單后囊嘉,接下來(lái)就是regist.html
文件:
<form action="/regist/" method="POST">
<table>
<tr>
<td>用戶(hù)名:</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>郵箱:</td>
<td><input type="email" name="email"></td>
</tr>
<tr>
<td>密碼:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td>確認(rèn)密碼:</td>
<td><input type="password" name="confirm"></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="提交"></td>
</tr>
</table>
</form>
再來(lái)看視圖函數(shù)regist
:
@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 u'注冊(cè)成功!'
return render_template('regist.html')
RegistForm
傳遞的是request.form
進(jìn)去進(jìn)行初始化温技,并且判斷form.validate
會(huì)返回用戶(hù)提交的數(shù)據(jù)是否滿(mǎn)足表單的驗(yàn)證。
渲染模板:
form
還可以渲染模板扭粱,讓你少寫(xiě)了一丟丟的代碼舵鳞,比如重寫(xiě)以上例子,RegistForm
表單代碼如下:
class RegistForm(Form):
name = StringField(u'用戶(hù)名:',validators=[length(min=4,max=25)])
email = StringField(u'郵箱:'validators=[email()])
password = StringField(u'密碼:',validators=[DataRequired(),length(min=6,max=10),EqualTo('confirm')])
confirm = StringField(u'確認(rèn)密碼:')
以上增加了第一個(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 u'注冊(cè)成功!'
return render_template('regist.html',form=form)
以上唯一的不同是在渲染模板的時(shí)候傳入了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>
Field常用參數(shù):
在使用Field
的時(shí)候慕淡,經(jīng)常需要傳遞一些參數(shù)進(jìn)去背伴,以下將對(duì)一些常用的參數(shù)進(jìn)行解釋?zhuān)?/p>
- label(第一個(gè)參數(shù)):
Field
的label的文本。 - validators:驗(yàn)證器峰髓。
- id:
Field
的id屬性傻寂,默認(rèn)不寫(xiě)為該屬性名。 - default:默認(rèn)值儿普。
- widget:指定的
html
控件崎逃。
常用Field:
BooleanField:布爾類(lèi)型的Field,渲染出去是
checkbox
眉孩。-
FileField:文件上傳Field个绍。
# forms.py from flask_wtf.file import FileField,FileAllowed,FileRequired class UploadForm(FlaskForm): avatar = FileField(u'頭像:',validators=[FileRequired(),FileAllowed([])]) # app.py @app.route('/profile/',methods=('POST','GET')) def profile(): form = ProfileForm() if form.validate_on_submit(): filename = secure_filename(form.avatar.data.filename) form.avatar.data.save(os.path.join(app.config['UPLOAD_FOLDER'],filename)) return u'上傳成功' return render_template('profile.html',form=form)
FloatField:浮點(diǎn)數(shù)類(lèi)型的Field勒葱,但是渲染出去的時(shí)候是
text
的input。IntegerField:整形的Field巴柿。同F(xiàn)loatField凛虽。
-
RadioField:
radio
類(lèi)型的input
。表單例子如下:# form.py class RegistrationForm(FlaskForm): gender = wtforms.RadioField(u'性別:',validators=[DataRequired()])
模板文件代碼如下:
<tr> <td> {{ form.gender.label }} </td> <td> {% for gender in form.gender %} {{ gender.label }} {{ gender }} {% endfor %} </td> </tr>
app.py
文件的代碼如下广恢,給gender
添加了choices
:@app.route('/register/',methods=['POST','GET']) def register(): form = RegistrationForm() form.gender.choices = [('1',u'男'),('2',u'女')] if form.validate_on_submit(): return u'success' return render_template('register.html',form=form)
-
SelectField:類(lèi)似于
RadioField
凯旋。看以下示例:# forms.py class ProfileForm(FlaskForm): language = wtforms.SelectField('Programming Language',choices=[('cpp','C++'),('py','python'),('text','Plain Text')],validators=[DataRequired()])
再來(lái)看
app.py
文件:@app.route('/profile/',methods=('POST','GET')) def profile(): form = ProfileForm() if form.validate_on_submit(): print form.language.data return u'上傳成功' return render_template('profile.html',form=form)
模板文件為:
<form action="/profile/" method="POST"> {{ form.csrf_token }} {{ form.language.label }} {{ form.language() }} <input type="submit"> </form>
StringField:渲染到模板中的類(lèi)型為
<input type='text'>
钉迷,并且是最基本的文本驗(yàn)證至非。PasswordField:渲染出來(lái)的是一個(gè)
password
的input
標(biāo)簽。TextAreaField:渲染出來(lái)的是一個(gè)
textarea
糠聪。
常用的驗(yàn)證器:
數(shù)據(jù)發(fā)送過(guò)來(lái)荒椭,經(jīng)過(guò)表單驗(yàn)證,因此需要驗(yàn)證器來(lái)進(jìn)行驗(yàn)證舰蟆,以下對(duì)一些常用的內(nèi)置驗(yàn)證器進(jìn)行講解:
- Email:驗(yàn)證上傳的數(shù)據(jù)是否為郵箱趣惠。
- EqualTo:驗(yàn)證上傳的數(shù)據(jù)是否和另外一個(gè)字段相等,常用的就是密碼和確認(rèn)密碼兩個(gè)字段是否相等身害。
- InputRequired:原始數(shù)據(jù)的需要驗(yàn)證味悄。如果不是特殊情況,應(yīng)該使用
InputRequired
塌鸯。 - Length:長(zhǎng)度限制侍瑟,有min和max兩個(gè)值進(jìn)行限制。
- NumberRange:數(shù)字的區(qū)間界赔,有min和max兩個(gè)值限制丢习,如果處在這兩個(gè)數(shù)字之間則滿(mǎn)足。
- Regexp:自定義正則表達(dá)式淮悼。
- URL:必須要是
URL
的形式。 - UUID:驗(yàn)證
UUID
揽思。
自定義驗(yàn)證字段:
使用validate_fieldname(self,field)
可以對(duì)某個(gè)字段進(jìn)行更加詳細(xì)的驗(yàn)證袜腥,如下:
class ProfileForm(FlaskForm):
name = wtforms.StringField('name',[validators.InputRequired()])
def validate_name(self,field):
if len(field.data) > 5:
raise wtforms.ValidationError(u'超過(guò)5個(gè)字符')
CSRF保護(hù):
在flask的表單中,默認(rèn)是開(kāi)啟了csrf
保護(hù)功能的钉汗,如果你想關(guān)閉表單的csrf
保護(hù)羹令,可以在初始化表單的時(shí)候傳遞csrf_enabled=False
進(jìn)去來(lái)關(guān)閉csrf
保護(hù)。如果你想關(guān)閉這種默認(rèn)的行為损痰。如果你想在沒(méi)有表單存在的請(qǐng)求視圖函數(shù)中也添加csrf
保護(hù)福侈,可以開(kāi)啟全局的csrf
保護(hù)功能:
csrf = CsrfProtect()
csrf.init_app(app)
或者是針對(duì)某一個(gè)視圖函數(shù),使用csrf.protect
裝飾器來(lái)開(kāi)啟csrf
保護(hù)功能卢未。并且如果已經(jīng)開(kāi)啟了全局的csrf
保護(hù)肪凛,想要關(guān)閉某個(gè)視圖函數(shù)的csrf
保護(hù)功能堰汉,可以使用csrf.exempt
裝飾器來(lái)取消本視圖函數(shù)的保護(hù)功能。
AJAX的CSRF保護(hù):
在AJAX
中要使用csrf
保護(hù)伟墙,則必須手動(dòng)的添加X-CSRFToken
到Header
中翘鸭。但是CSRF
從哪里來(lái),還是需要通過(guò)模板給渲染戳葵,而Flask
比較推薦的方式是在meta
標(biāo)簽中渲染csrf
就乓,如下:
<meta name="csrf-token" content="{{ csrf_token() }}">
如果要發(fā)送AJAX
請(qǐng)求,則在發(fā)送之前要添加CSRF
,代碼如下(使用了jQuery):
var csrftoken = $('meta[name=csrf-token]').attr('content')
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken)
}
}
})