Python---Flask--03--Web表单

尽管 Flask 的请求对象提供的信息足够用于处理 Web 表单,但有些任务很单调,而且要重复操作。比如,生成表单的 HTML 代码和验证提交的表单数据。

Flask-WTF(http://pythonhosted.org/Flask-WTF/)扩展可以把处理 Web 表单的过程变成一种愉悦的体验。这个扩展对独立的 WTForms(http://wtforms.simplecodes.com)包进行了包装,方便集成到 Flask 程序中。

请求跨域伪造保护

默认情况下,Flask-WTF 能保护所有表单免受跨站请求伪造(Cross-Site Request Forgery,CSRF)的攻击。恶意网站把请求发送到被攻击者已登录的其他网站时就会引发 CSRF 攻击。为了实现 CSRF 保护,Flask-WTF 需要程序设置一个密钥。Flask-WTF 使用这个密钥生成加密令牌,再用令牌验证请求中表单数据的真伪。

from flask import Flask
app = Flask(__name__)

app.config[SECRET_KEY] =  'aaaaaaaaaaaa'

app.config 字典可用来存储框架、扩展和程序本身的配置变量。使用标准的字典句法就能把配置值添加到 app.config 对象中。这个对象还提供了一些方法,可以从文件或环境中导入配置值。
SECRET_KEY 配置变量是通用密钥,可在 Flask 和多个第三方扩展中使用。如其名所示,加密的强度取决于变量值的机密程度。不同的程序要使用不同的密钥,而且要保证其他人不知道你所用的字符串。

表单类

创建forms.py

from flask_wtf import FlaskForm
from wtforms import BooleanField, StringField, SelectField, RadioField, FileField, PasswordField, SubmitField
from wtforms.validators import Email, EqualTo, DataRequired, ValidationError, Length, Regexp
from app import db
from app.models import User

class LoginForm(FlaskForm):
    """用户登录表单"""
    username = StringField(
        label='用户名',
        validators=[
            DataRequired('用户名不能为空')
        ],
        description='用户名',
        render_kw={
            'placeholder':'请输入用户名'
        }
    )

    password = PasswordField(
        label="密码",
        validators=[
            DataRequired('密码不能为空')
        ],
        description='密码',
        render_kw={
            'placeholder':'请输入密码'
        }
    )
    submit = SubmitField(
        "登录"
    )

    def validate_username(self, field):
        username = field.data
        user = User.query.filter_by(username=username).count()
        if user == 0:
            raise ValidationError('用户名不正确')


class RegisterForm(FlaskForm):
    username = StringField(
        label='用户名',
        validators=[
            DataRequired('用户名不能为空'),
            Length(5,20, '长度在5~20之间'),
        ],
        description='用户名',
        render_kw={
            'placeholder':'请输入用户名'
        }
    )
    email = StringField(
        label='邮箱',
        validators=[
            DataRequired('不能为空'),
            Email('请输入正确的格式')
        ],
        description='邮箱'
    )
    password = PasswordField(
        label='密码',
        validators=[
            DataRequired('不能为空'),
        ],
    )
    repassword = PasswordField(
        label='密码',
        validators=[
            DataRequired('不能为空'),
            EqualTo('password','两次密码不一致')
        ]
    )
    submit = SubmitField(
        '注册'
    )

    def validate_username(self, field):
        """验证用户是否重复"""
        username = field.data

        user_count = User.query.filter_by(username=username).count()

        if user_count >= 1:
            raise ValidationError('用户名重复')


    def validate_email(self, filed):
        """验证邮箱是否注册"""
        email = filed.data

        user_count = User.query.filter_by(email=email).count()
        if user_count >= 1:
            raise ValidationError('邮箱已经注册')

WTForms支持的HTML标准字段
|字段类型|说明|
|---|---|
|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 | 一组指定类型的字段|

WTForms验证函数
|验证函数|说明|
|---|---|
|Email | 验证电子邮件地址|
|EqualTo |比较两个字段的值;常用于要求输入两次密码进行确认的情况|
|IPAddress | 验证 IPv4 网络地址|
|Length | 验证输入字符串的长度|
|NumberRange | 验证输入的值在数字范围内|
|Optional | 无输入值时跳过其他验证函数|
|Required | 确保字段中有数据|
|Regexp | 使用正则表达式验证输入值|
|URL | 验证 URL|
|AnyOf | 确保输入值在可选值列表中|
|NoneOf | 确保输入值不在可选值列表中|

把表单渲染成HTML

@home.route('/login', methods=['GET', 'POST'])
def login():
    """登录"""
    form = LoginForm()
    if form.validate_on_submit():
        data = form.data
        user = User.query.filter_by(username=data['username']).first()
        if check_password_hash(user.password, data['password']):
            print(222222)
            login_user(user)
            print(1111)
            return redirect(url_for('home.index'))
        else:
            print(44444444)
            flash('登录失败','err')

    return render_template('/home/user/login.html', form=form)

例通过参数 form 传入模板,在模板中可以生成一个简单的表单,如下所示:

{{ form.hidden_tag() }} {{ form.name.label }} {{ form.name() }} {{ form.submit() }}

即便能指定 HTML 属性,但按照这种方式渲染表单的工作量还是很大,所以在条件允许的情况下最好能使用 Bootstrap 中的表单样式。Flask-Bootstrap 提供了一个非常高端的辅助函数,可以使用 Bootstrap 中预先定义好的表单样式渲染整个 Flask-WTF 表单,而这些操作只需一次调用即可完成。使用 Flask-Bootstrap,上述表单可使用下面的方式渲染:

{% import "bootstrap/wtf.html" as wtf %}
{{ wtf.quick_form(form) }}

import 指令的使用方法和普通 Python 代码一样,允许导入模板中的元素并用在多个模板中。导入的 bootstrap/wtf.html 文件中定义了一个使用 Bootstrap 渲染 Falsk-WTF 表单对象的辅助函数。wtf.quick_form() 函数的参数为 Flask-WTF 表单对象,使用 Bootstrap 的默认样式渲染传入的表单。hello.py 的完整模板如示例 4-3 所示。
templates/index.html:使用 Flask-WTF 和 Flask-Bootstrap 渲染表单

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %}

{{ wtf.quick_form(form) }}
{% endblock %}

模板的内容区现在有两部分。第一部分是页面头部,显示欢迎消息。这里用到了一个模板条件语句。Jinja2 中的条件语句格式为 {% if condition %}...{% else %}...{% endif %}。如果条件的计算结果为 True,那么渲染 if 和 else 指令之间的值。如果条件的计算结果为
False,则渲染 else 和 endif 指令之间的值。在这个例子中,如果没有定义模板变量 name,则会渲染字符串“Hello, Stranger!”。内容区的第二部分使用 wtf.quick_form() 函数渲染NameForm 对象。

你可能感兴趣的:(Python---Flask--03--Web表单)