4.1Flask-WTF

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è)passwordinput標(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-CSRFTokenHeader中翘鸭。但是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)
        }
    }
})
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拱烁,一起剝皮案震驚了整個(gè)濱河市生蚁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌戏自,老刑警劉巖守伸,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異浦妄,居然都是意外死亡尼摹,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)剂娄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蠢涝,“玉大人,你說(shuō)我怎么就攤上這事阅懦『投” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵耳胎,是天一觀的道長(zhǎng)惯吕。 經(jīng)常有香客問(wèn)我,道長(zhǎng)怕午,這世上最難降的妖魔是什么废登? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮郁惜,結(jié)果婚禮上堡距,老公的妹妹穿的比我還像新娘。我一直安慰自己兆蕉,他們只是感情好羽戒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著虎韵,像睡著了一般易稠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上包蓝,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天驶社,我揣著相機(jī)與錄音企量,去河邊找鬼。 笑死衬吆,一個(gè)胖子當(dāng)著我的面吹牛梁钾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播逊抡,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼姆泻,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了冒嫡?” 一聲冷哼從身側(cè)響起拇勃,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎孝凌,沒(méi)想到半個(gè)月后方咆,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蟀架,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年瓣赂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片片拍。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡煌集,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出捌省,到底是詐尸還是另有隱情苫纤,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布纲缓,位于F島的核電站卷拘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏祝高。R本人自食惡果不足惜栗弟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望褂策。 院中可真熱鬧横腿,春花似錦、人聲如沸斤寂。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)遍搞。三九已至,卻和暖如春器腋,著一層夾襖步出監(jiān)牢的瞬間溪猿,已是汗流浹背钩杰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留诊县,地道東北人讲弄。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像依痊,于是被迫代替她去往敵國(guó)和親避除。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容