对表单的定义,验证(服务器端),处理等操作
字段属性的名称会作为对应HTML input元素的name 属性以及ID属性值
WTForms会在表单提交后根据表单类中字段的类型对数据进行处理,转换成对应的Python类型
WTForms 输出的字段HTML代码只会包含id和name属性,属性值均为表单类中对应的字段属性名称
username = StringField('Username', render_kw={'placeholder': 'Your Username'})
通过render_kw 设置了placeholder HTMl属性
<input type="text" id="username" name="username" placeholder="Your Username">
调用字段时传入
class由于是保留字段,使用class_替代
form.username(style='width: 200px;', class_='bar')
通过添加括号使用关键字参数的形式传入字段
u'<input class="bar" id="username" name="username" style="width: 200px; " type="text">'
BooleanField
复选框,值会被处理为True 或者False
<input type="checkbox">
DataField
文本字段,值会被处理为datetime.date对象
<input type="text">
DateTimeField
文本字段,值会被处理为datetime.datetime对象
<input type="text">
FileField
文件上传字段
<input type="file">
FloatField
浮点数字段,值会被处理为浮点类型
<input type="text">
IntegerField
整数字段,值会被处理为整型
<input type="text">
RadioField
一组单选按钮
<input type="radio">
SelectField
下拉列表
<select><option></option></select>
SelectMultipleField
多选下拉列表
<select multiple><option></option></select>
SubmitField
提交按钮
<input type="submit">
StringField
文本字段
<input type="text">
HiddenField
隐藏文本字段
<input type="hidden">
PasswordField
密码文本字段
<input type="password">
TextAreaField
多行文本字段
<textarea></textarea>
字段标签
一个字典,用来设置对应的HTML 标签属性,比如传入{'placeholder': 'Your name'}
, 渲染后的HTML代码会将 标签的placeholder属性设置为Your name
一个列表,包含一系列验证器,会在表单提交后被逐一调用验证表单数据
message 参数用来传入自定义错误消息,乳沟没有设置则使用内置的英文错误消息
name = StringField('Your Name', validators=[DataRequired(message=u'名字不能空')])
input 标签中的type 方法设置为file,会在浏览器中渲染成一个文件上传字段
HTML5 中的accept属性也可以实现类型过滤
<input type="file" id="profile_pic" name="profile_pic" accept=".jpg, .jpeg, .png, .gif">
在input 标签中添加multiple 属性可以开启多选
创建表单时,直接使用Multiple File Field实现
from wtforms import MultipleFileField class MultiUploadForm(FlaskForm): photo = MultipleFileField('Upload Image', validators={DataRequired()})
secure_filename 函数会对文件名进行过滤,传递文件名作为参数,会过滤掉所有危险的字符
from werkzeug import secure_filename secure_filename('avatar! @#//#\\%$^&.jpg')
默认会过滤掉文件名中非ASCII字符,如果由非ASCII字符组成,会得到空文件名,为避免通常是使用统一的处理方式对上传的文件重新命名
def random_filename(filename): ext = os.path.splitext(filename)[1] new_filename = uuid.uuid4().hex + ext return new_filename
字符串或者可调用对象,用来为表单字段设置默认值
form标签声明中类型为submit的提交字段被单击时,会创建一个提交表单的HTTP请求,请求中包含表单各个字段的数据
使用POST方法提交表单,按照默认的编码类型,表单数据会被存储在请求主体
POST /basic HTTP/1.0 ... Content-Type: application/x-www-form-urlencoded Content-Length: 30
避免页面刷新和重载发送请求, 尽量不要让提交表单的POST请求作为最后一个请求,一般是在处理表单后返回一个重定向响应,这样会让浏览器重新发送一个新的GET请求到重定向的目标URL,最终,最后一个请求就变成了GET请求。
为区分表单, 需要设置不同的提交字段名称
class SigninForm(FlaskForm): username = StringField('Username', validators=[DataRequired(), Length(1, 20)]) password = PasswordField("Password", validators=[DataRequired(), Length(8, 128)]) submit1 = SubmitField('Sign in')
class RegisterForm(FlaskForm): username = StringField('Username', validators=[DataRequired(), Length(1, 20)]) email = StringField('Email', validators=[DataRequired(), Email(), Length(1, 254)]) password = PasswordField("Password", validators=[DataRequired(), Length(8, 128)]) submit2 = SubmitField('Register')
@app.route('/multi-form', methods=['GET', 'POST']) def multi_form(): signin_form = SigninForm() register_form = RegisterForm() if signin_form.submit1.data and signin_form.validate(): username = signin_form.username.data flash('%s', you just submit the Signin From. % username) return redirect(url_for('index')) if register_form.submit2.data and register_form.validate(): username = register_form.username.data flash('%s, you just submit the Register Form.' % username) return redirect(url_for('index')) return render_template('2form.html', signin_form=sigin_form, register_form=register_form)
然后表单实例通过不同的变量名传入到模板中
... <form method="post"> {{ signin_form.csrf_token }} {{ form_field(sigin_form.username) }} {{ form_field(sigin_form.password) }} {{ sigin_form.submit1 }} </form> <h2>Register Form</h2> <form method="post"> {{ register_form.csrf_token }} {{ form_field(register_form.username) }} {{ form_field(register_form.email) }} {{ form_field(register_form.password) }} {{ register_form.submit2 }} </form>
使用HTML5 内置验证属性
type, required, min, max, accept
<input type="text" name="username" required>
可以在定义表单的时候通过render_kw 传入,或是在表单渲染的时候传入
{{ form.username(required='') }}
也可以使用JavaScript实现表单验证
调用validate()方法,对字段逐个验证,若错误会存储到errors属性对应的字段中
class LoginForm(Form): username = StringField('Username', validators=[DataRequired()]) password = PasswordField('Password', validators=[DataRequired(), Length(8, 128)]) form = LoginForm(username='', password='123') form.validate() form.errors >>> false {'username': [u'This field is required.'], 'password': [u'Field must be at least 6 characters long.']}
通过调用request.method 获取
if request.method == 'POST' and form.validate(): pass
使用POST方法提交的表单,其数据会被Flask解析为一个字典,可以通过请求对象的form属性获取(request.form)
使用GET方法提交的表单的数据同样会被解析为字典,不过要通过请求对象的args属性获取(request.args)
from wtforms import IntegerField, SubmitField from wtforms.validators import ValidationError class FortyTwoForm(FlaskForm): answer = IntegerField('The Number') submit = SubmitField() def validate_answer(form, field): if field.data != 42: raise ValidationError('Must be 42.')
这里的validate_answer 用来验证对应字段
定义一个可以通用的验证器
from wtforms.validators import ValidationError def is_42(form, field): if field.data != 42: raise ValidationError('Must be 42') class FortyTwoForm(FlaskForm): answer = IntegerField('The Number', validators=[is_42]) submit = SubmitField()
集成了WTForms,可以在Flask中更方便的使用WTForms,附加了reCAPTHCA 支持
pipenv install flask-wtf
Flask-WTF定义表单时,仍然使用WTForms提供的字段类和验证器,创建方式是完全相同的,只不过表单类要集成Flask-WTF提供的Flask Form类, Flask Form 类继承自Form类,进行一些设置,并附加了类,并附加了一些辅助方法,以便与Flask集成
from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, SubmitField from wtforms.validators import DataRequired, Length class LoginForm(FlaskForm): username = StringField('Username', validators=[DataRequired()]) password = PasswordField('Password', validators=[DataRequired(), Length(8, 128)]) remember = BooleanField('Remember me') submit = SubmitField('Login')
配置键WTF_CSRF_ENABLED用来设置是否开启CSRF保护,默认为True,Flask-WTF会自动在实例化表单类时添加一个包含CSRF令牌值的隐藏字段,字段名为csrf_token
Flask-WTF 默认为每个表单启用了CSRF保护, 自动生成和验证CSRF令牌,需要程序密钥来进行签名
app.secret_key = 'secret string'