4. 表单

2018-10-29  本文已影响0人  ArtioL

4.1 HTML表单

先看一个简单的HTML表单

<form method="post">
  <label for="username">Username</label>
  <input type="text" name="username" placeholder="Hector Rivera">
  <br>
  <label for="password">Username</label>
  <input type="text" name="password" placeholder="19001130">
  <br>
  <input id="remember" name="remember" type="checkbox" checked>
  <input type="submit" name="submit" value="Login in">
</form>

4.2 使用Flask-wtf处理表单

$ pip install flask-wtf

Flask-wtf默认为每个表单启用CSRF保护,他会为我们自动生成和验证CSRF令牌,默认情况下 我们需要在程序内设置秘钥

app.secret_key = "secret string"

4.2.1 定义WTForms表单类

字段类 说明 对应的HTML表示
BooleanField 复选框,值会被处理为True或False <input type="checkbox">
DataField 文本字段,值会被处理为datetime.date对象 <input type="text">
DateTimeField 文本字段,值会被处理为datetime.datetime对象 <input type="text">
FielField 文件上传字段 <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>
SubmitFiled 提交按钮 <input type="submit">
StringField 文本字段 <input type="text">
HiddenField 隐藏文本字段 <input type="hidden">
PasswordFiled 密码文本字段 <input type="password">
TextAreaField 多行文本字段 <textarea></textarea>

实例化字段类型常用参数

参数 说明
label 字段label的值 也就是渲染后显示在输入字段前的文字
render_kw 一个字段 用来设置对应的HTML<INPUT>标签的属性, 比如传入{"placeholder": "Your Name"}, 渲染后的HTML代码或将<input>标签的placeholder属性设为Your Name
validators 一个列表 包含一些列的验证器 会在表单提交后逐一被调用验证表单数据
default 字符串或可调用对象, 用来为表单字段设置默认值

常用的WTForms验证器

验证器 说明
DataRequired(message=None) 验证数据是否有效
Email(message=None) 验证Email地址
EqualTo(filename, message=None) 验证两个字段值是否相等
InputRequired(message=None) 验证是否有数据
Length(min=-1, max=-1, messgae=None) 验证输入值是否在给定范围内
NumberRange(min=None, max=None, message=None) 验证输入数字是否在给定范围内
Optional(strip_whitespace=True) 允许输入值为空,并跳过其他验证
Regexp(regex, flags=0, message=None) 使用正则表达式验证输入值
URL(require_tld=True, messgae=None) 验证URL
AnyOf(value, message=None, values_formatter=None) 确保输入值在可选列表中
NoneOf(value, message=None, values_formatter=None) 确保输入值不在可选列表中

**下面我们来定义一个表单类 forms.py"

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("Log in")

4.2.2 输出HTML代码

form = LoginForm()
form.username

'<input id="username" name="username" type="text" value="">'

form.submit()

'<input id="submit" name="submit" type="submit" value="Submit">'

form.username.label()

<label for="username">Username</label>

form.submit.label()

<label for="submit">Submit</label>

1.使用render_kw属性

username = StringField("Username", render_kw={"placeholder": "Your Username"})

输出的label

<input type=text" id="username" name="username" placeholder="Your Username">

2.在调用字段时传入

  form.username(style="width:200px;', class_="bar")

class 是python的保留关键字 我们使用class_来替代class 但是我们不能修改name属性值

4.2.3 在模板中渲染表单

from forms import LoginForm

@app.route("/basic")
def basic():
  form = LoginForm()
  return render_template("login.html", form=form)

basic.html

<form method="post">
  {{ form.csrf_token }}<!-- 渲染CSRF令牌隐藏字段 -->
  {{ form.username.label }}{{ form.username }}<br>
  {{ form.password.label }}{{ form.password }}<br>
  {{ form.remember.label }}{{ form.remember }}<br>
  {{ form.submit }}<br>
</form>

渲染Bootstrap风格表单

<form method="post">
  {{ form.csrf_token }}<!-- 渲染CSRF令牌隐藏字段 -->
  <div class="form-group">
  {{ form.username.label }}{{ form.username(class="form-control") }}
  </div>
  <div class="form-group">
  {{ form.password.label }}{{ form.password(class="form-control") }}
  </div>
  <div class="form-check">
  {{ form.remember.label }}{{ form.remember(class="form-check-input") }}
  </div>
  {{ form.submit }}<br>
</form>

处理表单数据

从获取数据到保存数据大致会经过以下步骤:

4.3.1 提交表单

HTML表单中控制提交行为的树心

属性 默认值 说明
action 当前URL,即对应的Url 表单提交时发送请求的目标url
method get 提交表单的http请求方法,目前仅支持GET和POST方法
enctype application/x-www-form-urlencoded 表单数据的编码类型,当表单中包含文件上传字段时,需要设为mutipart/form-data,还可以设为纯文本类型text/plain

4.3.2 验证表单数据

from flask import request

@app.route("/basic", methods=["GET", "POST"]
def basic():
  form = LoginForm()
  if request.method == "POST" and form.validate():
     ...  # 处理POST请求
  return render_template("forms/basic.html", form=form

flask-wtf提供了validate_on_submit()方法合并了两个操作 因此可以优化为
from flask import request

@app.route("/basic", methods=["GET", "POST"]
def basic():
form = LoginForm()
if form.validate_on_submit():
... # 处理POST请求
return render_template("forms/basic.html", form=form)

# 表单验证与数据获取
from flask import Flask, render_template, flash, redirect, url_for
...

@app.route("/basic", methods=["GET", "POST"]
def basic():
  form = LoginForm()
  if form.validate_on_submit():
     username = form.username.data
     flash("Welcome home, %s!" % username
     return redirect(url_for("index"))
  return render_template("forms/basic.html", form=form)

4.3.3 在模板中渲染错误信息

<form method="post">
  {{ form.csrf_token }}<!-- 渲染CSRF令牌隐藏字段 -->
  <div class="form-group">
  {{ form.username.label }}{{ form.username(class="form-control") }}
  {% for message in form.username.errors %}
      <small class="error">{{messgae""</small><br>
  {% endfor %}
  </div>
  <div class="form-group">
  {{ form.password.label }}{{ form.password(class="form-control") }}
  {% for message in form.password.errors %}
      <small class="error">{{messgae""</small><br>
  {% endfor %}
  </div>
  <div class="form-check">
  {{ form.remember.label }}{{ form.remember(class="form-check-input") }}
  </div>
  {{ form.submit }}<br>
</form>

4.4 表单进阶实践

4.4.1 设置错误信息语言

from flask_wtf import FlaskForm
app = Flask(__name__)
app.config["WTF_I18N_ENABLED'] = False

class MyBaseForm(FlaskForm):
   class Meta:
      locales = ["zh"]

class HelloForm(MyBaseForm):
  name = StringField("Name", validtors=[DataRequired()])
  submit = SubmitField("提交")

我们需要将配置变量WTF_I18N_ENABLED设为False, 这会让Flask-WTF使用WTForms内置的错误信息翻译. 然后我们需要在自定义基类定义元类Meta.并在locales列表中加入中文。也可以在实例化表单类时通过meta关键字传入locales,比如:

form = MyForm(meta={"locales":["en_US", "en"]})

4.4.2 使用宏渲染表单

{% macro from_field(field) %}
{{ field.label }}<br>
{{ field(**kwargs) }}<br>
{% if field.errors %}
  {% for error in errors %}
    <small class="error">{{ error }}</small>
  {% endfor %}
{% endif %}
{% endmacro %}

宏调用

{ % from 'macros.html' import form_field %}
...
<form method="post">
  {{ form.csrf_token }}
  {{ form_field(form.username) }} 
  {{ form_field(form.password }}

4.4.3 自定义验证器

from wtforms import IntegerField, SubmitField
from wtforms.validators import ValidationError

class FortywoForm(FlaskForm):
  answer = IntegerField("The Number")
  submit = SubmitField(“提交")
  
  def validate_answer(form, field):
    if field.data != 42:
      raise ValidationError("Must be 42.')

当表单类包含以”validate_字段属性名"形式命名的方法时,在验证字段数据时会同时调用这个方法来验证对应的字段,这也是为什么表单类的属性名不能以validate开头.验证方法接收两个位置参数,依次为form和field,前者为表单类实例,后者为字段对象,我们可以通过field.data获取字段数据,这2个参数将在验证表单时被调用传入. 验证出错时抛出从wtforms.validators模块导入的ValidationError异常,传入错误消息作为参数.因此这种方法仅用来验证特定的表单类字段,所以又称为行内验证器(in-line validator)。

from wtforms.validators import ValidationError

def is_42(form, field):
  if field.data != 42:
    raise ValidationError("Must be 42")

class FortywoForm(FlaskForm):
  answer = IntegerField("The Number", validators=[is_42])
  submit = SubmitField(“提交")

我们通常需要让验证器支持传入参数来对验证器进行函数调用。这里我们使用闭包实现

def is_42(message=None):
  if message is None:
    message = "Must be 42."
  def _is_42(form, field)
    if field.data != 42:
      raise ValidationError(message)
  return _is_42

class FortywoForm(FlaskForm):
  answer = IntegerField("The Number", validators=[is_42()])
  submit = SubmitField(“提交")

4.4.4 文件上传

出于安全考虑.除了常规的CSRF防范,我们还需要重点注意下面的问题

1.定义上传表单

from flask_wtf.file import FileField, FileRequired, FileAllowed
from flask_wtf import FlaskForm

class UploadForm(FlaskForm):
  photo = FileField("Upload Image", validators=[FileRequired(), FileAllowed(['jpg', 'jpeg', 'png', 'gif'])])
  submit = SubmitField()
验证器 说明
FileRequired(message=None) 验证是否包含文件对象
FileAllowed(upload_set, meaasge=None) 用来验证文件类型,upload_set参数用来传入包含允许的文件后缀名列表

除了验证文件的类型 我们通常还需要对文件大小进行验证 通过FLASK的内置的配置变量MAX_CONTENT_LENGTH我们可以限制请求报文的最大长度 单位为字节(byte) 比如我们将最大长度限制为3M。
app.config["MAX_CONTENT_LENGTH"] = 3 * 1024 * 1024

上一篇 下一篇

猜你喜欢

热点阅读