4.1Flask-WTF

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è)passwordinput標(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-CSRFTokenHeader中。但是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)
        }
    }
})
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容