Flask-WTF
Flask-WTF是簡(jiǎn)化了WTForms操作的一個(gè)第三方庫。WTForms表單的兩個(gè)主要功能是驗(yàn)證用戶提交數(shù)據(jù)的合法性以及渲染模板。當(dāng)然還包括一些其他的功能:CSRF保護(hù),文件上傳等。安裝Flask-WTF默認(rèn)也會(huì)安裝WTForms,因此使用以下命令來安裝Flask-WTF:
pip install flask-wtf
表單驗(yàn)證:
安裝完Flask-WTF后。來看下第一個(gè)功能,就是用表單來做數(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的長度應(yīng)該在4-25之間。email必須要滿足郵箱的格式。password長度必須在6-10之間,并且應(yīng)該和confirm相等才能通過驗(yàn)證。
寫完表單后,接下來就是regist.html文件:
<form action="/regist/" method="POST">
<table>
<tr>
<td>用戶名:</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>
再來看視圖函數(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ì)返回用戶提交的數(shù)據(jù)是否滿足表單的驗(yàn)證。
渲染模板:
form還可以渲染模板,讓你少寫了一丟丟的代碼,比如重寫以上例子,RegistForm表單代碼如下:
class RegistForm(Form):
name = StringField(u'用戶名:',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ù),用來在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變量了。
接下來看下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)行解釋:
- label(第一個(gè)參數(shù)):
Field的label的文本。 - validators:驗(yàn)證器。
- id:
Field的id屬性,默認(rèn)不寫為該屬性名。 - default:默認(rèn)值。
- widget:指定的
html控件。
常用Field:
BooleanField:布爾類型的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ù)類型的Field,但是渲染出去的時(shí)候是
text的input。IntegerField:整形的Field。同F(xiàn)loatField。
-
RadioField:
radio類型的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:類似于
RadioField。看以下示例:# forms.py class ProfileForm(FlaskForm): language = wtforms.SelectField('Programming Language',choices=[('cpp','C++'),('py','python'),('text','Plain Text')],validators=[DataRequired()])再來看
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:渲染到模板中的類型為
<input type='text'>,并且是最基本的文本驗(yàn)證。PasswordField:渲染出來的是一個(gè)
password的input標(biāo)簽。TextAreaField:渲染出來的是一個(gè)
textarea。
常用的驗(yàn)證器:
數(shù)據(jù)發(fā)送過來,經(jīng)過表單驗(yàn)證,因此需要驗(yàn)證器來進(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:長度限制,有min和max兩個(gè)值進(jìn)行限制。
- NumberRange:數(shù)字的區(qū)間,有min和max兩個(gè)值限制,如果處在這兩個(gè)數(shù)字之間則滿足。
- 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'超過5個(gè)字符')
CSRF保護(hù):
在flask的表單中,默認(rèn)是開啟了csrf保護(hù)功能的,如果你想關(guān)閉表單的csrf保護(hù),可以在初始化表單的時(shí)候傳遞csrf_enabled=False進(jìn)去來關(guān)閉csrf保護(hù)。如果你想關(guān)閉這種默認(rèn)的行為。如果你想在沒有表單存在的請(qǐng)求視圖函數(shù)中也添加csrf保護(hù),可以開啟全局的csrf保護(hù)功能:
csrf = CsrfProtect()
csrf.init_app(app)
或者是針對(duì)某一個(gè)視圖函數(shù),使用csrf.protect裝飾器來開啟csrf保護(hù)功能。并且如果已經(jīng)開啟了全局的csrf保護(hù),想要關(guān)閉某個(gè)視圖函數(shù)的csrf保護(hù)功能,可以使用csrf.exempt裝飾器來取消本視圖函數(shù)的保護(hù)功能。
AJAX的CSRF保護(hù):
在AJAX中要使用csrf保護(hù),則必須手動(dòng)的添加X-CSRFToken到Header中。但是CSRF從哪里來,還是需要通過模板給渲染,而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)
}
}
})