Flask-WTF表单的使用方法


Posted in Python onJuly 12, 2019

flask_wtf是flask框架的表单验证模块,可以很方便生成表单,也可以当做json数据交互的验证工具,支持热插拔。

安装

pip install Flask-WTF

Flask-WTF其实是对wtforms组件的封装,使其支持对flask框架的热插拔。

简单使用

# app.py
from flask import Flask, current_app, request, render_template
from forms import MyForm

app = Flask(__name__,template_folder='static/html')
@app.route('/',methods=['GET','POST'])
def login():
  form = MyForm()
  if form.validate_on_submit():
    return 'OK'
  return render_template('forms/index.html', form=form)
if __name__ == '__main__':
  app.run(host='127.0.0.1', port=80, debug=True)

# forms.py
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired

class MyForm(FlaskForm):
  name = StringField('name', validators=[DataRequired()])

# forms/index.html
<form method="POST" action="/">
{{ form.csrf_token }}
{{ form.name.label }} {{ form.name(size=20) }}
<input type="submit" value="Go">
</form>

flask_wtf定义字段

flask_wtf完全使用wtforms组件的字段模型,wtforms对字段的定义在fields模块;又分为core和simple,core模块定义了普通使用的字段,simple在core模块的基础上扩展了一些字段,这些字段会自动进行字段级别的校验。

字段类型

# core.py
__all__ = (
  'BooleanField', 'DecimalField', 'DateField', 'DateTimeField', 'FieldList',
  'FloatField', 'FormField', 'IntegerField', 'RadioField', 'SelectField',
  'SelectMultipleField', 'StringField',
)

常用字段说明:

  • BooleanField:布尔类型,如Flask,True
  • StringField:字符串类型
  • DecimalField:小数点文本字段,如:‘1.23'
  • DateField:日期字段,格式:'%Y-%m-%d'
  • DateTimeField:日期字段,格式:'%Y-%m-%d %H:%M:%S'
  • FieldList:统一字段类型组成列表,如:FieldList(StringField('Name', [validators.required()]))
  • FloatField:浮点数类型
  • IntegerField:整形
  • SelectMultipleField:多选框
  • RadioField:单选框

simple.py

  • TextAreaField:文本域,可接受多行输入
  • PasswordField:密码字段,输入的不会直接在浏览器明文显示
  • FileField:上传文件,但不会处理验证文件,需要手动处理
  • HiddenField:隐藏字段
  • SubmitField:按钮
  • TextField:字符串类型的别名,弃用

表单定义

# 参数:
class UserAdminForm(FlaskForm):
  username = StringField(label='用户名', validators=[DataRequired(),Length(4,20)])
  password_hash = PasswordField(label='密码',validators=[DataRequired(),Length(4,20)])
  limit = SelectField(label='用户权限',
            choices=[('guest', '所有权限'),
                 ('operation', '可读可写不可删除'),
                 ('management', '可读不可写')],
            default='guest') # 权限

# 字段一般的参数
# label:字段的名字
# default:默认
# validators:字段的验证序列
# description:字段的描述
# choices:SelectField和他的子类有的字段,选择框,多选一

字段的验证序列

字段的参数validators可以指定提交表单的验证序列,按照从左到右的顺序,默认的可选验证在wtforms.validators模块,已经封装的验证方法有:

__all__ = (
  'DataRequired', 'data_required', 'Email', 'email', 'EqualTo', 'equal_to',
  'IPAddress', 'ip_address', 'InputRequired', 'input_required', 'Length',
  'length', 'NumberRange', 'number_range', 'Optional', 'optional',
  'Required', 'required', 'Regexp', 'regexp', 'URL', 'url', 'AnyOf',
  'any_of', 'NoneOf', 'none_of', 'MacAddress', 'mac_address', 'UUID'
)

模块中大小写有对应的方式,如DataRequired对应data_required。

  • DataRequired/data_required:验证数据是否真实存在,即不能为空,必须是非空白字符串,否则触发StopValidation错误。
  • InputRequired/input_required:和DataRequired的区别在于可以是空白字符串;
  • Required/required:data_required的别名
  • Email/email:验证符合邮件的格式,只有最基本的验证;
  • EqualTo/equal_to:比较两个字段的值,比如密码和确认密码,如果不相等就触发错误,equal_to(field,message),需要输入另一个字段的名字。
  • IPAddress/ip_address:验证是否是ip地址,默认验证IPV4地址。
  • MacAddress/mac_address:验证是否符合mac格式;
  • UUID:是否是uuid格式;
  • URL/url:验证是否符合url格式;
  • Regexp/regexp:用提供的正则表达式验证字段;Regexp(r"")
  • Length/length:设置字段值的长度,Length(min,max);
  • NumberRange/number_range:设置一个数字字段的取值范围,可以针对浮点数和小数;NumberRange(min,max)
  • Optional/optional:允许字段为空并停止验证;
  • NoneOf/none_of:将传入的数据和无效的比较,是抛出异常;Noneof(values).
  • Anyof/any_of:将传入的数据和预设的数据比较,不是异常。Anyof(values).

自定义字段验证

如果默认的验证序列不满足我们的要求,我们可以通过继承的方式自定义字段。

from wtforms.validators import DataRequired,Length,StopValidation
class NewStringField(StringField):
  """
  自定义一个新的字段
  """
  def pre_validate(self, form):
    """验证方法,在validators验证序列之前"""
    x:str = form.name.data
    if not x.startswith('g'):
      raise StopValidation("your data must be startswith 'g'")

  def post_validate(self, form, validation_stopped):
    """
    验证方法,在validators验证序列之后
    :param form:该字段所属的表单对象
    :param validation_stopped:前面验证序列的结果,True表示验证通过,False表示验证失败
    :return:
    """
    if not validation_stopped:
      raise ValueError("验证失败了!")
    pass

触发StopValidation异常会停止验证链;

自定义表单验证

一般来说,如果对表单有额外需要的验证,一般自定义表单的额外的验证方法而不是重新自定义新的字段,而form已经为我们提供了这种方法。

看Form对象的源码:

def validate(self):
  """
  Validates the form by calling `validate` on each field, passing any
  extra `Form.validate_<fieldname>` validators to the field validator.
  """
  extra = {}
  for name in self._fields:
    inline = getattr(self.__class__, 'validate_%s' % name, None)
    if inline is not None:
      extra[name] = [inline]

  return super(Form, self).validate(extra)

Form对象调用validate函数时会自动寻找validate_%s的方法添加到验证序列,并在原先字段的验证序列验证完毕后执行。

class MyForm(FlaskForm):
  name = StringField('name', validators=[DataRequired(), Length(4,20)])
  def validate_name(self, field):
    print(field.data)
    if hasattr(self, 'name') and len(self.name.data) > 5:
      print(self.name.data)
      return True
    raise ValidationError('超过5个字符')

# 在自定义的验证方法中,抛出异常使用ValidationError,validate会自动捕捉。

表单对象

flask_wtf推荐使用Form对象的子类FlaskForm代替,该对象提供了所有表单需要的属性和方法。那么Form对象是如何自动实现表单功能的呢?

分析FlaskForm对象源码:

class FlaskForm(Form):
  class Meta(DefaultMeta):
    def wrap_formdata(self, form, formdata):
      pass

  def __init__(self, formdata=_Auto, **kwargs):
    csrf_enabled = kwargs.pop('csrf_enabled', None)
    pass
  def is_submitted(self):
    pass
  def validate_on_submit(self):
    pass
  def hidden_tag(self, *fields):
    pass
  def validate(self):
    pass
  • FlaskForm内部定义了一个Meta类,该类添加csrf保护的一些方法,所以创建表单的时候一定要导入FlaskForm而不是Form.
  • is_submitted:检查是否有一个活跃的request请求;
  • validate_on_submit:调用is_submitted和validate方法,返回一个布尔值,用来判断表单是否被提交;
  • validate:字段级别的验证,每个字段都有一个validate方法,FlaskForm调用validate会对所有的字段调用validate方法验证,如果所有的验证都通过返回Ture,否则抛出异常。
  • hidden_tag:获取表单隐藏的字段;
  • wrap_formdata:获取request中的form,每次form对象初始化时会执行该函数从request获取form。

重要属性

  • form.data:字段名字和值组成的字典;
  • form.errors:验证失败的信息字典,在调用validate_on_submit方法后才有效;
  • form.name.data:字段name的值;
  • form.name.type:字段name的类型

常用场景

登录验证

# froms.py
class UserPasswordForm(FlaskForm):
  """
  登录提交的表单
  """
  username = StringField('User', validators=[DataRequired()])
  password = PasswordField('Password', validators=[DataRequired()])

# form.html
<form method="POST" action="/">
{{ form.csrf_token }}
{{ form.username.label }} {{ form.username(size=20) }}
{{ form.password.label }} {{ form.password }}
<input type="submit" value="Go">
</form>

# views.py
@app.route('/login',methods=['GET','POST'])
def login():
  form = UserPasswordForm()
  if form.validate_on_submit():
    # 验证表单
    if form.username.data == "xiaoming" and form.password.data == '123':
      return 'OK'
  return render_template('forms/index.html', form=form)

ajax请求转化表单

有时候我们没有html页面的表单,只有ajax请求的数据交互,但是想借用Form来定义接口和验证接收的数据,如果ajax的请求方法是('POST', 'PUT', 'PATCH', 'DELETE')中的一种,FlaskForm会自动从request对象中调用request.form和request.get_json()方法来接收数据,因此这种方式十分方便。注意:get方法不再其中。

# form.py
class MyForm(FlaskForm):
  name = StringField('name', validators=[DataRequired(), Length(4,20)])
# view.py
@app.route('/form',methods=['GET','POST'])
def form():
  if request.method != "GET":
    form = MyForm() # form会获取请求数据
    print(form.data)
    return 'ok'
  return ''
# test.py
import requests as req
import json

class ProTest():
  baseurl = 'http://127.0.0.1:80'
  def test_form(self):
    url = self.baseurl + '/form'
    rsp = req.post(url,json={'name':'hhhh'})
    # rsp = req.get(url,json={'name':'hhhh'})
if __name__ == '__main__':
  test = ProTest()
  test.test_form()

form启用csrf保护

默认csrf保护是开启的,只要在html文件中添加{{ form.csrf_token }},app必须设置SECRET_KEY参数。

# 禁用保护
form = Form(csrf_enabled=False)
# 或配置app时
WTF_CSRF_ENABLED = False

一般数据csrf保护

同理必须设置SECRET_KEY参数。

from flask_wtf.csrf import CsrfProtect
csrf = CsrfProtect()

def create_app():
  app = Flask(__name__)
  csrf.init_app(app)

# 模板中添加一个隐藏域
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<meta name="csrf-token" content="{{ csrf_token() }}">
# 如果是ajax请求,可以在脚本中
var csrftoken = "{{ csrf_token() }}"
# 然后每个请求添加 X-CSRFToken 头部

# 全局禁用csrf
WTF_CSRF_CHECK_DEFAULT = False

# 对一些视图跳过csrf检查
@csrf.exempt
@app.route('/foo', methods=('GET', 'POST'))
def my_handler():
  return 'ok'

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python访问纯真IP数据库脚本分享
Jun 29 Python
python中实现k-means聚类算法详解
Nov 11 Python
Python通过matplotlib绘制动画简单实例
Dec 13 Python
PyQt5每天必学之关闭窗口
Apr 19 Python
Python字符串逆序的实现方法【一题多解】
Feb 18 Python
Python实现微信翻译机器人的方法
Aug 13 Python
原来我一直安装 Python 库的姿势都不对呀
Nov 11 Python
Python连接Hadoop数据中遇到的各种坑(汇总)
Apr 14 Python
在python中使用nohup命令说明
Apr 16 Python
浅析Python模块之间的相互引用问题
Feb 26 Python
Python爬取酷狗MP3音频的步骤
Feb 26 Python
python开发人人对战的五子棋小游戏
May 02 Python
解决Python中pandas读取*.csv文件出现编码问题
Jul 12 #Python
python的debug实用工具 pdb详解
Jul 12 #Python
Flask配置Cors跨域的实现
Jul 12 #Python
python调用webservice接口的实现
Jul 12 #Python
python3模拟实现xshell远程执行liunx命令的方法
Jul 12 #Python
Python实现蒙特卡洛算法小实验过程详解
Jul 12 #Python
教你如何编写、保存与运行Python程序的方法
Jul 12 #Python
You might like
PHP实现用户认证及管理完全源码
2007/03/11 PHP
遍历指定目录下的所有目录和文件的php代码
2011/11/27 PHP
destoon实现首页显示供应、企业、资讯条数的方法
2014/07/15 PHP
CentOS6.5 编译安装lnmp环境
2014/12/21 PHP
php操纵mysqli数据库的实现方法
2016/09/18 PHP
thinkPHP多表查询及分页功能实现方法示例
2017/07/03 PHP
javascript Object与Function使用
2010/01/11 Javascript
JqGrid web打印实现代码
2011/05/31 Javascript
Javascript遍历table中的元素示例代码
2014/07/08 Javascript
express的中间件basicAuth详解
2014/12/04 Javascript
JavaScript中用于四舍五入的Math.round()方法讲解
2015/06/15 Javascript
详解JavaScript对W3C DOM模版的支持情况
2015/06/16 Javascript
Bootstarp风格的toggle效果分享
2016/02/23 Javascript
JavaScript实现body内任意节点的自定义属性功能示例
2017/09/18 Javascript
Angular学习教程之RouterLink花式跳转
2018/05/03 Javascript
小程序识别身份证,银行卡,营业执照,驾照的实现
2019/11/05 Javascript
jsonp格式前端发送和后台接受写法的代码详解
2019/11/07 Javascript
python实现类的静态变量用法实例
2015/05/08 Python
在Python的一段程序中如何使用多次事件循环详解
2017/09/07 Python
Django中使用celery完成异步任务的示例代码
2018/01/23 Python
Python远程视频监控程序的实例代码
2019/05/05 Python
在django view中给form传入参数的例子
2019/07/19 Python
Python如何基于selenium实现自动登录博客园
2019/12/16 Python
python代码实现将列表中重复元素之间的内容全部滤除
2020/05/22 Python
Django后端分离 使用element-ui文件上传方式
2020/07/12 Python
html5仿支付宝密码框的实现代码
2017/09/06 HTML / CSS
巴西家用小家电购物网站:Polishop
2016/08/07 全球购物
vivo智能手机官方商城:vivo
2016/09/22 全球购物
Michael Kors澳大利亚官网:世界知名的奢侈饰品和成衣设计师
2020/02/13 全球购物
2014全国两会学习心得体会1000字
2014/03/10 职场文书
推广普通话演讲稿
2014/05/23 职场文书
文秘专业应届生求职信
2014/05/26 职场文书
店铺转让协议书
2014/12/02 职场文书
2014年煤矿安全工作总结
2014/12/04 职场文书
暂停营业通知
2015/04/25 职场文书
JavaScript 中for/of,for/in 的详细介绍
2021/11/17 Javascript