Flask开发系列之Web表单

简单示例

from flask import Flask, request, render_templateapp = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def home():return render_template('home.html')@app.route('/signin', methods=['GET'])
def signin_form():return render_template('form.html')@app.route('/signin', methods=['POST'])
def signin():# receive the data from submitusername = request.form['username']password = request.form['password']if username == 'admin' and password == 'password':return render_template('signin-ok.html', username=username)return render_template('form.html', message='Bad username or password', username=username)if __name__ == '__main__':app.run()

<html>
<head><title>Please Sign In</title>
</head>
<body>{% if message %}<p style="color:red">{{ message }}</p>{% endif %}<form action="/signin" method="post"><p>Please sign in:</p><p><input name="username" placeholder="Username" value="{{ username }}"></p><p><input name="password" placeholder="Password" type="password" value="{{ password }}"></p><p><button type="submit">Sign In</button></p></form>
</body>
</html>

form.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>hello,{{ username }}</h1>
</body>
</html>

signin-ok

跨站请求伪造保护

默认情况下,Flask-WTF 能保护所有表单免受跨站请求伪造(Cross-Site Request Forgery,
CSRF)的攻击。恶意网站把请求发送到被攻击者已登录的其他网站时就会引发 CSRF 攻击。

为了实现 CSRF 保护,Flask-WTF 需要程序设置一个密钥。Flask-WTF 使用这个密钥生成
加密令牌,再用令牌验证请求中表单数据的真伪。设置密钥的方法如示例:

app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'

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

表单类

使用 Flask-WTF 时,每个 Web 表单都由一个继承自 Form 的类表示。这个类定义表单中的
一组字段,每个字段都用对象表示。字段对象可附属一个或多个验证函数。验证函数用来
验证用户提交的输入值是否符合要求。

from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField,TextAreaField,HiddenFieldfrom wtforms.validators import DataRequired, Length, Email
class NameForm(FlaskForm):title = StringField('Title', validators=[DataRequired()])category = StringField('Category')content = TextAreaField('Content')post_id = HiddenField('post_id')submit = SubmitField('submit')

StringField 构造函数中的可选参数 validators 指定一个由验证函数组成的列表,在接受
用户提交的数据之前验证数据。验证函数 Required() 确保提交的字段不为空。

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       确保输入值不在可选值列表中

表单渲染

两种方式:

  • flask-bootstrap渲染
  • 一般渲染(常用)

flask-bootstrap渲染

flask-boostrap是bootstrap的flask扩张,它可以提供wtf.html文件中的form_field函数来进行渲染:

{% import "bootstrap/wtf.html" as wtf %}
<form class="form" method="POST">{{ form.hidden_tag() }}{{ wtf.form_field(form.title) }}{{ wtf.form_field(form.content) }}...{{ wtf.form_field(form.submit) }}
</form>

注意,如果有多个隐藏字段,可以使用form.hidden_tag()渲染所以隐藏字段。
另外Flask-WTF支持跨站请求伪造保护,表单类创建时会自动创建一个CSRF字段,你需要在表单里渲染这个字段:{{ form.csrf_token }}。
还有一种快速渲染方式:

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

一般渲染(常用)

直接在对应的html模版中引入字段:

{% extends "base.html" %}
{% block title %}Flask{% endblock %}
{% block body %}
<form class="form" method="POST">{{ form.hidden_tag() }}{{ form.title.label }}{{ form.title() }}{{ form.content.label }}{{ form.content() }}{{ form.submit() }}
</form>
{% endblock %}

<html>
<head>{% block head %}<title>{% block title %}{% endblock %} - My Application</title>{% endblock %}
</head>
<body>{% block body %}{% endblock %}
</body>
</html>

base.html

另外还可以在字段中加入有些属性,比如要加入class:

form.title(class_="form-control")

转化为html的效果:

<input type="text" name="title" value="title" class="form-control" id="title" />

上面的渲染如果有很多字段的话,一个个写出来会觉得很繁琐,在实际开发中,可以创建一个_form.html文件来处理。
_form.html

{% macro render(form) -%}{% for field in form %}{% if field.type in ['CSRFTokenField', 'HiddenField'] %}<!--CSR和隐藏字段-->{{ field() }}{% elif field.type == "BooleanField" %}<!--Boolean类型字段--><div class="checkbox"><label>{{ field() }} {{ field.label }}</label></div>{% elif field.type == "RadioField" %}<!--Radio类型字段-->{{ field.label }}{% for subfield in field %}<div class="radio"><label>{{ subfield }} {{ subfield.label }}</label></div>{% endfor %}{% else %}<div class="clearfix {% if field.errors %}has-error{% endif %} form-group"><!--剩下的一般类型字段-->{{ field.label }}{% if field.type == "TextAreaField" %}{{ field(class_="form-control", rows=10) }}{% else %}{{ field(class_="form-control") }}{% endif %}{% if field.errors or field.help_text %}<span class="help-block">{% if field.errors %}{{ field.errors|join(' ') }}{% else %}{{ field.help_text }}{% endif %}</span>{% endif %}</div>{% endif %}
{% endfor %}{% endmacro %}

接着在对应的html模版中进行引用 :

{% import "_form.html" as forms %}<!--引入_form--><form method="POST" action="">{{ forms.render(form) }}<!--使用_form对form来进行渲染-->

一个简单的例子

from flask import Flask,render_template
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from flask_bootstrap import Bootstrap
from wtforms.validators import Requiredapp = Flask(__name__)
app.config["SECRET_KEY"] = "12345678"
bootstrap = Bootstrap(app)class NameForm(FlaskForm):name = StringField('What is your name?', validators=[Required()])submit = SubmitField('Submit')@app.route('/', methods=['GET', 'POST'])
def index():name = None#实例化表单类form = NameForm()# 如果提交的数据验证通过,则返回Trueif form.validate_on_submit():name = form.name.dataform.name.data = ''return render_template('index.html', form=form, name=name)if __name__ == '__main__':app.run(debug=True)# Flask提供的render_template函数把Jinja2模板引擎集成到了程序中。# render_template函数的第一个参数是模板的文件名。# 随后的参数都是键值对,表示模板中变量对应的真实值。

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1>
</div>
{{ wtf.quick_form(form) }}
{% endblock %}

index.html

{% extends "bootstrap/base.html" %}
{% block title %}Flasky{% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation"><div class="container"><div class="navbar-header"><button type="button" class="navbar-toggle"data-toggle="collapse" data-target=".navbar-collapse"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a class="navbar-brand" href="/">Flasky</a></div><div class="navbar-collapse collapse"><ul class="nav navbar-nav"><li><a href="/">Home</a></li></ul></div></div>
</div>
{% endblock %}
{% block content %}
<div class="container">{% block page_content %}{% endblock %}
</div>
{% endblock %}

base.html

重定向和用户会话

from flask import Flask, render_template, session, redirect, url_for
@app.route('/', methods=['GET', 'POST'])
def index():form = NameForm()if form.validate_on_submit():session['name'] = form.name.datareturn redirect(url_for('index'))return render_template('index.html', form=form, name=session.get('name'))

前面到的代码存在一个可用性问题。用户输入名字后提交表单,然后点击浏览器的刷
新按钮,会看到一个莫名其妙的警告,要求在再次提交表单之前进行确认。之所以出现这
种情况,是因为刷新页面时浏览器会重新发送之前已经发送过的最后一个请求。如果这个
请求是一个包含表单数据的 POST 请求,刷新页面后会再次提交表单。大多数情况下,这并
不是理想的处理方式。
很多用户都不理解浏览器发出的这个警告。基于这个原因,最好别让 Web 程序把 POST 请
求作为浏览器发送的最后一个请求。
这种需求的实现方式是,使用重定向作为 POST 请求的响应,而不是使用常规响应。重定
向是一种特殊的响应,响应内容是 URL,而不是包含 HTML 代码的字符串。浏览器收到
这种响应时,会向重定向的 URL 发起 GET 请求,显示页面的内容。这个页面的加载可能
要多花几微秒,因为要先把第二个请求发给服务器。除此之外,用户不会察觉到有什么不
同。现在,最后一个请求是 GET 请求,所以刷新命令能像预期的那样正常使用了。这个技
巧称为 Post/ 重定向 /Get 模式。
但这种方法会带来另一个问题。程序处理 POST 请求时,使用 form.name.data 获取用户输
入的名字,可是一旦这个请求结束,数据也就丢失了。因为这个 POST 请求使用重定向处
理,所以程序需要保存输入的名字,这样重定向后的请求才能获得并使用这个名字,从而构建真正的响应。
程序可以把数据存储在用户会话中,在请求之间“记住”数据。用户会话是一种私有存
储,存在于每个连接到服务器的客户端中。用户会话,它是请求上下文中的变量,名为 session ,像标准的 Python 字典一样操作。

注意:默认情况下,用户会话保存在客户端 cookie 中,使用设置的 SECRET_KEY 进
行加密签名。如果篡改了 cookie 中的内容,签名就会失效,会话也会随之
失效。

代码分析

局部变量 name 被用于存储用户在表单中输入的名字。这个变量现
在保存在用户会话中,即 session['name'] ,所以在两次请求之间也能记住输入的值。
现在,包含合法表单数据的请求最后会调用 redirect() 函数。 redirect() 是个辅助函数,
用来生成 HTTP 重定向响应。 redirect() 函数的参数是重定向的 URL,这里使用的重定向
URL 是程序的根地址,因此重定向响应本可以写得更简单一些,写成 redirect('/') ,但
却会使用 Flask 提供的 URL 生成函数 url_for() 。推荐使用 url_for() 生成 URL,因为这
个函数使用 URL 映射生成 URL,从而保证 URL 和定义的路由兼容,而且修改路由名字后
依然可用。
url_for() 函数的第一个且唯一必须指定的参数是端点名,即路由的内部名字。默认情
况下,路由的端点是相应视图函数的名字。在这个示例中,处理根地址的视图函数是
index() ,因此传给 url_for() 函数的名字是 index 。
最后一处改动位于 render_function() 函数中,使用 session.get('name') 直接从会话中读
取 name 参数的值。和普通的字典一样,这里使用 get() 获取字典中键对应的值以避免未找
到键的异常情况,因为对于不存在的键, get() 会返回默认值 None 。

参考链接:
1.https://greyli.com/flask-form-create-and-render/
2.WTForms官方文档
3.http://flask.pocoo.org/docs/1.0/patterns/wtforms/
4.https://stackoverflow.com/questions/20905188/flask-wtforms-validation-always-false
5.《FlaskWeb开发:基于python的Web应用开发实战

转载于:https://www.cnblogs.com/-wenli/p/11042961.html

Flask开发系列之Web表单相关推荐

  1. python-flask(二)集成bootstrap、集成web表单、集成邮件发送

    文章目录 一.flask集成bootstrap 1. 什么是Bootstrap? 2. Flask中如何集成Bootstrap? 3. Flask-Bootstrap实现了什么? 二.Flask中集成 ...

  2. Web框架——Flask系列之WTF表单验证练习(七)

    一.Web表单 web表单是web应用程序的基本功能. 它是HTML页面中负责数据采集的部件.表单有三个部分组成:表单标签.表单域.表单按钮.表单允许用户输入数据,负责HTML页面数据采集,通过表单将 ...

  3. Flask Web表单

    title: flask学习笔记 subtitle: 3. flask Web表单 date: 2018-12-14 10:17:28 --- Web表单 HTML表单是用户和web站点或应用程序之间 ...

  4. Flask之Web表单使用

    Web表单使用 @(Flask) request对象包含客户端发出的所有请求信息. request.form能获取POST请求中提交的表单数据. 使用的包 Flask-WTF可以把处理Web表单的过程 ...

  5. Flask实践——microblog WEB表单 (3)

    WEB表单 配置 创建microblog/config.py CSRF_ENABLED = True SECRET_KEY = 'you-will-never-guess' 修改app/__init_ ...

  6. 用php实现一个简易的web表单生成器,PHP—Web表单生成器

    1.实例: 2. 需求分析 在项目的实际开发中,经常需要设计各种各样表单.直接编写HTML表单虽然简单,但修改.维护相对麻烦. 因此,可以利用PHP实现一个Web表单生成器,使其可以根据具体的需求定制 ...

  7. 项目开发中常用JS表单取值方法

    项目开发中常用JS表单取值方法 一.常用表单基本取值方法(form1为表单名称,TextBox1为控件ID,以文本框为例,html控件与web服务器控件是一样的)         1.form1.Te ...

  8. struts2官方 中文教程 系列六:表单验证

    先贴个本帖的地址,以免被爬:struts2教程 官方系列六:表单验证  即 http://www.cnblogs.com/linghaoxinpian/p/6906720.html 下载本章节代码 介 ...

  9. 用php实现一个简易的web表单生成器,网络编程PHP Web表单生成器案例分析

    本文实例讲述了PHP Web表单生成器.分享给大家供大家参考,具体如下: 1.实例: 2. 需求分析 在项目的实际开发中,经常需要设计各种各样表单.直接编写HTML表单虽然简单,但修改.维护相对麻烦. ...

最新文章

  1. [SPOJ-COT]Count on a tree
  2. mysql union order by_MySQL order by 在 union 中使用实例分析
  3. mysql-odbc的zip安装方法_win10环境下mysql-odbc的zip安装方法
  4. 程序员必知的 Python 陷阱与缺陷列表
  5. 超详细!上线一个机器学习项目你需要哪些准备?
  6. Android Studio 导入 so 简明教程:通过一个示例让你理解整个过程
  7. python的一些问题解决方法
  8. python api加快交易速度_使用Python3的pipedriveapi将交易输出限制为1000个交易
  9. 推荐:万能模板,十分钟打造电商首焦Banner
  10. 机器学习算法总结之支持向量机(五)
  11. Megcup 2017 决赛第一题 规则
  12. hssfrow 单元格样式_poi的各种单元格样式以及一些常用的配置
  13. 写高性能 Web 应用程序的 10 个技巧 转自微软资料 .
  14. EOS合约开发 - 钱包篇
  15. 目标检测-RCNN系列
  16. php毕业论文选题系统,基于THINKPHP的毕业论文选题系统的设计
  17. 中国数码纺织印花染料行业运行态势与投资前景预测报告2022-2027
  18. 航班管理系统(最全最细)
  19. 康奈尔大学统计学数据科学与计算机学院,美国康奈尔大学统计学专业.pdf
  20. Codeforces Round #838 (Div. 2)题解

热门文章

  1. 基于 Java NIO 实现简单的 HTTP 服务器
  2. Java虚拟机学习(8):查看JVM参数及值的命令行工具
  3. 从零开始学习jQuery (四) 使用jQuery操作元素的属性与样式
  4. Java 包(package)
  5. Spring MVC Hello World 例子
  6. Steve Lin:如何撰写一篇优秀的SIGGRAPH论文
  7. 有哪些LSTM(Long Short Term Memory)和RNN(Recurrent)网络的教程?
  8. OpenCV学习笔记(一)(二)(三)(四)(五)
  9. 互联网公司GitHub repo 语言使用情况
  10. 程序员笔试面试基础知识资料整理