Flask模板--表单验证

1. 表单验证

  • 表单验证意义
  • 数据录入及逐层传输过程中,为了防止被篡改或劫持,一般要进行加密和严格的校验。表单作为数据的入口,一般会对数据的格式和长度等作初步的校验。
  • flask扩展Flask-WTF继承了WTFforms,使用它可以在flask中更方便的使用WTForms。Flask-WTF将表单数据解析、CSRF保护、文件上传等功能与Flask集成。
  • 原则:用户输入的一切数据皆不可信.
  • 不使用插件

flask多种响应方式

from flask import Flask, render_template, request, abort, jsonify, make_response, Response
import re
import json

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('demo.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('demo.html')
    else:
        phone = request.form.get('phone')
        pwd= request.form.get('pwd')
        confirm_pwd = request.form.get('confirm_pwd')
        
        
        if not phone:
            # return render_template('*.html')
            return render_template('demo.html', msg='手机号码不能为空'), 412, {'content-type':'text/html;charset=UTF-8'}
        elif not re.match(r'^1[3,5,7,8,9]\d{9}$', phone): 
            # abort()抛出异常后捕获
            abort(413, description = '手机号格式错误')
        elif phone != '13345678888':
            # abort(Response)
            res = Response(render_template('demo.html', msg='号码对应账户不存在'), status=412, content_type='text/html;charset=UTF-8')
            abort(res)
            
        if not pwd:
            # return str
            return '密码不能为空'
        elif len(pwd)<6:
            abort(412, description = '密码不安全')
        
        if confirm_pwd != pwd:
            abort(412, description = '确认密码不正确')  
            
        # return "{'phone': '13345678888'}" 
        # return jsonify({'phone': '13345678888'}), '200 OK', {'content-type':'application/json', 'server':'nginx'}
        return make_response(json.dumps({'phone': '13345678888'}), '201 xx', {"content-type": "application/json"})
        
 
if __name__ == '__main__':
    app.run(debug=True)
    

2. flask-WTF

  • 官方文档:http://www.pythondoc.com/flask-wtf/
2.1 安装
pip install Flask-WTF
2.2 详解
  • Form重要参数
validate: 验证主函数
errors: 获取错误信息
data: 获取所有数据
process: 验证数据,BaseForm里
  • 表单常用字段
StringField 文本字段
TextAreaField 多行文本字段
PasswordField 密码文本字段
HiddenField 隐藏文本字段
DateField 文本字段,值为 datetime.date 格式
DateTimeField 文本字段,值为 datetime.datetime 格式
IntegerField 文本字段,值为整数
DecimalField 文本字段,值为 decimal.Decimal
FloatField 文本字段,值为浮点数
BooleanField 复选框,值为 True 和 False
RadioField 一组单选框
SelectField 下拉列表
SelectMultipleField 下拉列表,可选择多个值
FileField 文件上传字段
SubmitField 表单提交按钮
FormField 把表单作为字段嵌入另一个表单
FieldList 一组指定类型的字段
  • 表单字段常用参数
label = None  字段的标签,即input框前的提示名
validators = None  用来储存各种验证器的列表,验证表单数据时会逐一进行调取
filters = tuple() 过滤
description = ''  字段描述,常用与帮助文档,类似django的django的help_text
id = None  为字段添加id值,默认为id为字段名
default = None  是一个字符串或可调用对象,为表单字段设置默认值,选择框常用
widget = None  html控件,用来定义input框的类型,需要导入widgets
render_kw = None  用来设置input框的属性

  • 常见验证函数
Email 验证电子邮件地址
EqualTo 比较两个字段的值;常用于要求输入两次密码进行确认的情况
IPAddress 验证 IPv4 网络地址
Length 验证输入字符串的长度
NumberRange 验证输入的值在数字范围内
Optional 无输入值时跳过其他验证函数
Required 确保字段中有数据
Regexp 使用正则表达式验证输入值
URL 验证 URL
AnyOf 确保输入值在可选值列表中
NoneOf 确保输入值不在可选值列表中
DataRequired 错误提示
InputRequired 验证原始输入的值
2.3 code
# demo.py

from flask import Flask, render_template, request, make_response, Response
from helpers.forms import RegisterForm
import os


app = Flask(__name__)

app.config['SECRET_KEY'] = os.urandom(24)


@app.route('/register', methods=['GET', 'POST'])
def login():
    form = RegisterForm(request.form)
    if request.method == 'GET':
        return render_template('demo.html', form=form)
    if form.validate():
        return 'success'
    return f'error: {form.errors}'

if __name__ == '__main__':
    app.run(debug=True)
# forms.py

from flask_wtf import FlaskForm
from wtforms import StringField,PasswordField,IntegerField,TextAreaField,RadioField, DecimalField, SelectField, DateField, SelectMultipleField
from wtforms.validators import DataRequired, Email, NumberRange, Regexp, Length, EqualTo


class RegisterForm(FlaskForm):
    # 属性名要和前端html的name保持一致
    
    phone = StringField(label='手机号码', validators=[
        Regexp(r'^1[3,5,7,8,9]\d{9}$',message='手机号码格式错误'), 
        DataRequired('手机号码不能为空')],render_kw={'placeholder': '请输入手机号'})
    
    pwd = PasswordField(label='密码', validators=[
        Regexp(r'[a-zA-Z0-9]+',message='仅支持字母和数字'),
        Length(6,32, message='密码仅支持6-32位长度'), 
        DataRequired('密码不能为空')])
    
    confirm_pwd = PasswordField(label='确认密码', validators=[
        EqualTo('pwd', message='确认密码与密码不一致')])
    
    # render_kw={"class": "age"} 给标签添加class="age"属性
    # default 设置默认值
    age = IntegerField(label='年龄',validators=[
        NumberRange(min=1, max=200, message='年龄仅支持1-200'), 
        DataRequired('年龄不能为空')], render_kw={"class": "age"}, default=18)
    
    email = StringField(label='邮箱', validators=[
        Email(message='邮箱格式不合法'),
        DataRequired('邮箱不能为空')])
    
    gender = RadioField(label='性别', choices=[('m', 'Male'),('f', 'Female')])
    
    # DecimalField(label='身高', places=1) 必须输入数值,保留一位小数
    height = DecimalField(label='身高(cm): ', places=1)
    
    birthday = DateField('出生日期', format='%Y-%m-%d') 
    
    # 单选
    job = SelectField('职业:', choices=[
        ('teacher', '教师'),
        ('doctor', '医生'),
        ('engineer', '工程师'),
        ('lawyer', '律师')
    ])
      
    # 多选
    hobby = SelectMultipleField('爱好:', choices=[
        ('0', '吃饭'),
        ('1', '睡觉'),
        ('2', '敲代码')
    ])
    
    comment = TextAreaField(label='备注', validators=[
        Length(0,512)])

    button = SubmitField(label='注册')
<form action="{{ url_for('login') }}" method="POST">
    {{ form.hidden_tag() }}
    <label>手机号:</label><input type="text" name="phone", value=''>
    <label>密码:</label><input type="password" name="pwd", value=''>
    <label>确认密码:</label><input type="password" name="confirm_pwd", value=''>
    <label>年龄:</label><input type="password" name="confirm_pwd", value=''>
    <input type="submit" name="login_btn", value="登陆">
</form>
  • 前端模板写法
<form action="{{ url_for('login') }}" method="POST">
    {{ form.hidden_tag() }}
    {{ form.phone.label }} {{ form.phone }}
    {{ form.pwd.label }}{{ form.pwd }}
    {{ form.confirm_pwd.label }}{{ form.confirm_pwd }}
    {{ form.age.label }}{{ form.age }}
    {{ form.email.label }} {{ form.email }}
    {{ form.gender.label }} {{ form.gender }}
    {{ form.height.label }}{{ form.height }}
    {{ form.birthday.label }}{{ form.birthday }}
    {{ form.birthday.label }}{{ form.birthday }}
    {{ form.job.label }}{{ form.job }}
    {{ form.hobby.label }} {{ form.hobby }}
    {{ form.button }}
</form>

3. Form传参方式

  • 源码
class Form(with_metaclass(FormMeta, BaseForm)):
    """
    Declarative Form base class. Extends BaseForm's core behaviour allowing
    fields to be defined on Form subclasses as class attributes.

    In addition, form and instance input data are taken at construction time
    and passed to `process()`.
    """
    Meta = DefaultMeta

    def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):
        """
        :param formdata:
            Used to pass data coming from the enduser, usually `request.POST` or
            equivalent. formdata should be some sort of request-data wrapper which
            can get multiple parameters from the form input, and values are unicode
            strings, e.g. a Werkzeug/Django/WebOb MultiDict
        :param obj:
            If `formdata` is empty or not provided, this object is checked for
            attributes matching form field names, which will be used for field
            values.
        :param prefix:
            If provided, all fields will have their name prefixed with the
            value.
        :param data:
            Accept a dictionary of data. This is only used if `formdata` and
            `obj` are not present.
        :param meta:
            If provided, this is a dictionary of values to override attributes
            on this form's meta instance.
        :param `**kwargs`:
            If `formdata` is empty or not provided and `obj` does not contain
            an attribute named the same as a field, form will assign the value
            of a matching keyword argument to the field, if one exists.
        """
.....
formdata
  • formdata=request.form
curl 'http://127.0.0.1:5000/register' \
 -H 'Content-Type: application/x-www-form-urlencoded'  \
 -H 'Cookie: session=eyJjc3JmX3Rva2VuIjoiMTBmNDY5MDU4MTI2MjMzZWNhNWFmYjZkZTk1MzMzYTZkOTA3YjdjZCJ9.YHO6pQ.9j30UratBCdFtPbvhFx090tRHv8'   \
 -d 'csrf_token=IjEwZjQ2OTA1ODEyNjIzM2VjYTVhZmI2ZGU5NTMzM2E2ZDkwN2I3Y2Qi.YHO60Q.fjfjDuiWikLgMFNyQcM1BEM4Iiw&phone=13623452345&pwd=123456&confirm_pwd=123456&age=12'
data
  • data=request.form
curl 'http://127.0.0.1:5000/register' \
 -H 'Content-Type: application/x-www-form-urlencoded'  \
 -H 'Cookie: session=eyJjc3JmX3Rva2VuIjoiMTBmNDY5MDU4MTI2MjMzZWNhNWFmYjZkZTk1MzMzYTZkOTA3YjdjZCJ9.YHO6pQ.9j30UratBCdFtPbvhFx090tRHv8'   
 -d 'csrf_token=IjEwZjQ2OTA1ODEyNjIzM2VjYTVhZmI2ZGU5NTMzM2E2ZDkwN2I3Y2Qi.YHO60Q.fjfjDuiWikLgMFNyQcM1BEM4Iiw&phone=13623452345&pwd=123456&confirm_pwd=123456&age=12'
  • data=request.json
curl 'http://127.0.0.1:5000/register'   \
  -H "Content-Type:application/json"   \
  -H 'Cookie: session=eyJjc3JmX3Rva2VuIjoiMTBmNDY5MDU4MTI2MjMzZWNhNWFmYjZkZTk1MzMzYTZkOTA3YjdjZCJ9.YHO6pQ.9j30UratBCdFtPbvhFx090tRHv8'   \
  -d '{"csrf_token":"IjEwZjQ2OTA1ODEyNjIzM2VjYTVhZmI2ZGU5NTMzM2E2ZDkwN2I3Y2Qi.YHO60Q.fjfjDuiWikLgMFNyQcM1BEM4Iiw","phone":"13623452345","pwd":"123456","confirm_pwd":"123456","age":12}'
obj对象
  • obj=request.json
curl 'http://127.0.0.1:5000/register'   \
  -H "Content-Type:application/json"   \
  -H 'Cookie: session=eyJjc3JmX3Rva2VuIjoiMTBmNDY5MDU4MTI2MjMzZWNhNWFmYjZkZTk1MzMzYTZkOTA3YjdjZCJ9.YHO6pQ.9j30UratBCdFtPbvhFx090tRHv8'   \
  -d '{"csrf_token":"IjEwZjQ2OTA1ODEyNjIzM2VjYTVhZmI2ZGU5NTMzM2E2ZDkwN2I3Y2Qi.YHO60Q.fjfjDuiWikLgMFNyQcM1BEM4Iiw","phone":"13623452345","pwd":"123456","confirm_pwd":"123456","age":12}'

4. 自定义验证器

  • 方式一:自定义
  • 方式二:参照源码,并修改源码使用

# forms.py

from flask_wtf import FlaskForm
from wtforms import StringField,PasswordField,IntegerField,TextAreaField,RadioField, DecimalField, SelectField, DateField, SelectMultipleField, SubmitField
from wtforms.validators import DataRequired, Email, NumberRange, Regexp, Length, EqualTo, ValidationError
import re



class RegisterForm(FlaskForm):
    # 属性名要和前端html的name保持一致
    
    
    pwd = PasswordField(label='密码', validators=[
        Regexp(r'[a-zA-Z0-9]+',message='仅支持字母和数字'),
        Length(6,32, message='密码仅支持6-32位长度'), 
        DataRequired('密码不能为空')])
    
    confirm_pwd = PasswordField(label='确认密码', validators=[
        EqualTo('pwd', message='确认密码与密码不一致')])
    

    

     
# 方式一:自定义手机号码校验器
def validate_phone(phone):
    regex = re.compile(r'^1[3,5,7,8,9]\d{9}$')
    match = regex.match(phone)
    if not match:
        return False
    return True
    
# 方式二: 拓展源码

class ValidatePhone(object):
    def __init__(self, message=None):
        if message is None:
            self.message = '手机号格式不正确'
        self.message = message

    def __call__(self, form, field):
        self.regex = re.compile(r'^1[3,5,7,8,9]\d{9}$')
        match = self.regex.match(field.data)
        if not match:
            raise ValidationError(message='Mobile Error')
        return match
# demo.py
@app.route('/register', methods=['GET', 'POST'])
def register():
    form = RegisterForm(request.form)
    if request.method == 'GET':
        return render_template('index.html', form=form)

    if form.validate() and validate_phone(request.form.get('phone')): 
        return '注册成功'
    return f'error: {form.errors}'
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值