Flask - WTF和WTForms創建表單


Flask - WTF和WTForms創建表單

一. Flask-WTF

Flask-WTF是集成WTForms,並帶有 csrf 令牌的安全表單和全局的 csrf 保護的功能。
每次我們在建立表單所創建的類都是繼承與flask_wtf中的FlaskForm,而FlaskForm是繼承WTForms中forms。

1.創建基礎表單

class LoginForm(FlaskForm):
    username = StringField()
    password = PasswordField()
    remember_me = BooleanField(label='Keep me logged in')

2.CSRF保護

任何使用FlaskForm創建的表單發送請求,都會有CSRF的全部保護,在對應的template中HTML渲染表單時,可以加入form.csrf_token:

<form method="post">
    {{ form.csrf_token }}
</form>

但是如果模板中沒有表單,則可以使用一個隱藏的input標簽加入csrf_token。

<form method="post">
    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
</form>

3.驗證表單

在視圖處理程序中驗證請求:

def login():
    form = LoginForm()
    if form.validate_on_submit():
        return redirect('/success')
    return render_template('login.html', form=form)

# 使用validate_on_submit 來檢查是否是一個 POST 請求並且請求是否有效。

或者下面這種方式:

new_lf = LoginForm(formdata=request.form)
        if new_lf.validate():
            return "登陸成功"
        else:
            return render_template("login.html", lf=new_lf)

4.文件上傳

Flask-WTF 提供 FileField 來處理文件上傳,它在表單提交后,自動從 flask.request.files 中抽取數據。FileField 的 data 屬性是一個 Werkzeug FileStorage 實例。

from werkzeug import secure_filename
from flask_wtf.file import FileField

class PhotoForm(Form):
    photo = FileField('Your photo')

@app.route('/upload/', methods=('GET', 'POST'))
def upload():
    form = PhotoForm()
    if form.validate_on_submit():
        filename = secure_filename(form.photo.data.filename)
        form.photo.data.save('uploads/' + filename)
    else:
        filename = None
    return render_template('upload.html', form=form, filename=filename)

注意:在 HTML 表單的 enctype 設置成 multipart/form-data,如下:

5.驗證碼

Flask-WTF 通過 RecaptchaField 也提供對驗證碼的支持:

rom flask_wtf import Form, RecaptchaField
from wtforms import TextField

class SignupForm(Form):
    username = TextField('Username')
    recaptcha = RecaptchaField()

還需要配置一下信息:

# 字段					# 配置

RECAPTCHA_PUBLIC_KEY	# 必須公鑰

RECAPTCHA_PRIVATE_KEY	# 必須私鑰

RECAPTCHA_API_SERVER	# 可選驗證碼API服務器

RECAPTCHA_PARAMETERS	# 可選 一個 JavaScript(api.js)參數的字典

RECAPTCHA_DATA_ATTRS	# 可選 一個數據屬性項列表 https://developers.google.com/recaptcha/docs/display

二. WTForms

WTForms是一個Flask集成的框架,或者是說庫。用於處理瀏覽器表單提交的數據。它在Flask-WTF 的基礎上擴展並添加了一些隨手即得的精巧的幫助函數,這些函數將會使在 Flask 里使用表單更加有趣。

1. field字段

WTForms支持HTML字段:

字段類型:			說明:

StringFidle			文本字段, 相當於type類型為text的input標簽

TextAreaField		多行文本字段

PasswordField		密碼文本字段

HiddenField			隱藏文本字段

DateField			文本字段, 值為datetime.date格式

DateTimeFieeld		文本字段, 值為datetime.datetime格式

IntegerField		文本字段, 值為整數

DecimalField		文本字段, 值為decimal.Decimal

FloatField			文本字段, 值為浮點數

BooleanField		復選框, 值為True 和 False

RadioField			一組單選框

SelectField			下拉列表

SelectMultipleField	下拉列表, 可選擇多個值

FileField			文件上傳字段

SubmitField			表單提交按鈕

FormFiled			把表單作為字段嵌入另一個表單

FieldList			子組指定類型的字段

2.Validators驗證器

WTForms可以支持很多表單的驗證函數:

Email
驗證是電子郵件地址


EqualTo
比較兩個字段的值; 常用於要求輸入兩次密鑰進行確認的情況


IPAddress
驗證IPv4網絡地址


Length
驗證輸入字符串的長度


NumberRange
驗證輸入的值在數字范圍內


Optional
無輸入值時跳過其它驗證函數


DataRequired
確保字段中有數據


Regexp
使用正則表達式驗證輸入值


URL
驗證url


AnyOf
確保輸入值在可選值列表中


NoneOf
確保輸入值不在可選列表中

3.自定義Validators驗證器

第一種: in-line validator(內聯驗證器)

也就是自定義一個驗證函數,在定義表單類的時候,在對應的字段中加入該函數進行認證。下面的my_length_check函數就是用於判name字段長度不能超過50.

def my_length_check(form, field):
    if len(field.data) > 50:
        raise ValidationError('Field must be less than 50 characters')

class MyForm(Form):
    name = StringField('Name', [InputRequired(), my_length_check])

第二種:通用且可重用的驗證函數

一般是以validate開頭,加上下划線再加上對應的field字段(validate_filed),瀏覽器在提交表單數據時,會自動識別對應字段所有的驗證器,然后執行驗證器進行判斷。

class RegistrationForm(FlaskForm):
    email = StringField('Email', validators=[DataRequired(), Length(1, 60), Email()])
    username = StringField('Username', validators=[DataRequired(), Length(1, 60),
        Regexp('^[A-Za-z][A-Za-z0-9_.]*$', 0, 'username must have only letters, numbers dots or underscores')])
    password = PasswordField('Password', validators=[DataRequired(), EqualTo('password2', message='password must match')])
    password2 = PasswordField('Confirm password', validators=[DataRequired()])

    def validate_email(self, field):
        if User.objects.filter(email=field.data).count() > 0:
            raise ValidationError('Email already registered')

    def validate_username(self, field):
        if User.objects.filter(username=field.data).count() > 0:
            raise ValidationError('Username has exist')

第三種:比較高級的validators

class Length(object):
    def __init__(self, min=-1, max=-1, message=None):
        self.min = min
        self.max = max
        if not message:
            message = u'Field must be between %i and %i characters long.' % (min, max)
        self.message = message

    def __call__(self, form, field):
        l = field.data and len(field.data) or 0
        if l < self.min or self.max != -1 and l > self.max:
            raise ValidationError(self.message)

length = Length

4.Widget組件

下面可以以登錄界面為實例:

login.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms import validators
from wtforms import widgets

class LoginForm(Form):
    name = simple.StringField(
        label='用戶名',
        validators=[
            validators.DataRequired(message='用戶名不能為空.'),
        ],
        widget=widgets.TextInput(),
        render_kw={'class': 'form-control'}
    )
    pwd = simple.PasswordField(
        label='密碼',
        validators=[
            validators.DataRequired(message='密碼不能為空.'),
        ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )

三. 簡單的登錄驗證表單實例

from flask import Flask, request, render_template, redirect
from wtforms.fields import simple, core
from wtforms import Form, validators

app = Flask(__name__)
app.config["DEBUG"] = True


class LoginForm(Form):
    username = simple.StringField(
        label="用戶名:",
        validators=[
            validators.DataRequired(message="用戶名不能為空"),
            validators.Length(min=4, max=10, message="用戶名不能小於%(min)d,不能大於%(max)d")
        ],  # 聲明校驗方式
        id="username",
        # widget=widgets.FileInput(),
        render_kw={"class": "my_class"},
    )

    password = simple.PasswordField(
        label="密碼:",
        validators=[
            validators.Length(max=10, min=6, message="password不能小於%(min)d,不能大於%(max)d"),
            validators.Regexp("\d+", message="密碼只能是數字")
        ],
        id="pwd",
        render_kw={"style": "width:300px;"}
    )

    sub = simple.SubmitField(
        label="登陸",
        render_kw={"class": "bs"}
    )


@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "GET":
        lf = LoginForm()
        return render_template("login.html", lf=lf)
    else:
        new_lf = LoginForm(formdata=request.form)
        if new_lf.validate():
            return "登陸成功"
        else:
            return render_template("login.html", lf=new_lf)


class RegForm(Form):
    username = simple.StringField(
        label="用戶名:",
        validators=[
            validators.Length(min=4, max=10, message="用戶名不能小於%(min)d,不能大於%(max)d")
        ]
    )

    password = simple.PasswordField(
        label="密碼:",
        validators=[
            validators.Length(max=10, min=6, message="password不能小於%(min)d,不能大於%(max)d")
        ]
    )

    repassword = simple.PasswordField(
        label="確認眼神:",
        validators=[
            validators.EqualTo("password", message="眼神未確認")
        ]
    )

    email = simple.StringField(
        label="郵箱",
        validators=[
            validators.Email(message="郵箱格式不符")
        ]
    )

    gender = core.RadioField(
        label="性別:",
        choices=[
            (1, "女"),
            (2, "男")
        ],
        coerce=int
    )

    hobby = core.SelectMultipleField(
        label="嗜好",
        choices=[
            (1, "小姐姐"),
            (2, "小蘿莉"),
            (3, "小正太"),
            (4, "小哥哥")
        ],
        coerce=int
    )

    sub = simple.SubmitField(
        label="登陸",
        render_kw={"class": "bs"}
    )


@app.route("/reg", methods=["GET", "POST"])
def reg():
    if request.method == "GET":
        regf = RegForm()
        return render_template("reg.html", regf=regf)

    else:
        new_regf = RegForm(formdata=request.form)
        if new_regf.validate():
            print(new_regf.data.get("gender"))
            print(new_regf.data.get("hobby"))
            return new_regf.data.get("username")
        else:
            return render_template("reg.html", regf=new_regf)


if __name__ == '__main__':
    app.run()

參考鏈接:

1.https://wtforms.readthedocs.io/en/stable/

2.https://flask-wtf.readthedocs.io/en/stable/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM