avatar

Flask学习教程二——web表单详解

2.1 应用的基本结构

一、安装

二、应用的基本结构

处理URL和函数之间关系的程序成为路由

三、模板

模板继承

1. base.html

Jinja2使用block和endblock指令在基模板中定义内容区块,定义了三个block:head、title和body,且title包含在head中

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

2. 衍生模板

调用super(),引用基模板中同名区块里的内容

{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style>
</style>
{% endblock %}
{% block body %}
<h1>Hello, world!</h1>
{% endblock%}

样例

from flask_bootstrap import Bootstrap
from flask import Flask,render_template

app = Flask(__name__)
bootstrap = Bootstrap(app)

@app.route("/<name>")
def hello(name):
return render_template("user.html",name=name)

if __name__=="__main__":
app.debug=True
app.run()

在setting/project structure中add flask_bootstrap的路径image-20200428130849333

{% 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">
<div class="page-header">
<h1>Hello,{{ name }}</h1>
</div>
</div>
{% endblock %}

运行结果:

image-20200428130957457

自定义错误页面

@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'),404

@app.errorhandler(500)
def internal_server_error(e):
return render_template('500.html'),500

简化模板继承

base.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">
<div class="page-header">
{% block page_content %}{% endblock %}
</div>
</div>
{% endblock %}

404.html

{% extends "base.html" %}
{% block title %}Flasky - Page Not Found{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Not Found</h1>
</div>
{% endblock %}

user.html

{% extends "base.html" %}
{% block title %}Flasky - Page Not Found{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Hello, {{name}}</h1>
</div>
{% endblock %}

统一页面,简化继承

四、

模板只能使信息从服务器流向用户,而由HTML创建的Web表单可以将用户填写的信息提交给服务器,通常使用POST请求,并通过request.form访问

Flask-WTF拓展把处理web表单的过程进行集成

4.1 配置

配置密钥,Flask-WTF根据密钥生成加密签名,防止表单遭到跨站请求伪造(CSRF,cross-site request forgery)攻击。

# 配置Flask-WTF
app=Flask(__name__)
app.config['SECRET_KEY']='hard to guess string'



## 4.2 表单类

Web表单继承自FlaskForm类,验证函数```validators```用于验证用户提交的数据是否有效

```python
# 定义表单类
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

class NameForm(FlaskForm): # 含有文本字段name和提交按钮submit
name = StringField('What is your name?', validators=[DataRequired()])
submit = SubmitField('Submit')



### 表1:WTForms支持的HTML标准字段

| 字段对象 | 说明 |
| -------- | ---- |
| BooleanField | 复选框,值为True和False |
| DateField | 文本字段,值为datetime.date格式 |
| DateTimeField | 文本字段,值为datetime.datetime格式 |
| FileField | 文本字段,值为decima.Decimal格式 |
|FileField|文本上传字段|
| HiddenField | 隐藏的文本字段 |
| MultipleFileField | 多文件上传字段 |
| FieldList | 一组指定类型的字段 |
| FloatField | 文本字段,值为浮点数 |
| FormField | 把一个表单作为字段嵌入另一个表单 |
| IntegerField | 文本字段,值为整数 |
| PasswordField | 密码文本字段 |
| RadioField | 一组单选按钮 |
| SelectField | 下拉列表 |
| SelectField | 下拉列表,可选择多个值 |
| SelectMultipleField | 表单提交按钮 |
| StringField | 文本字段 |
| TextAreaField | 多行文本字段 |

### 表2:WTForms验证函数

| 验证函数 | 说明 |
| ------------- | ------------------------------------------------------ |
| DataRequired | 确保转换类型后字段中有数据 |
| Email | 验证电子邮件地址 |
| EqualTo | 比较两个字段的值;常用于要求输入两次密码进行确认的情况 |
| InputRequired | 确保转换类型前字段中由数据 |
| IPAddress | 验证IPv4网络地址 |
| Length | 验证输入字符串的长度 |
| MacAddress | 验证MAC地址 |
| NumberRange | 验证输入的值在数字范围之内 |
| Optional | 允许字段中没有输入,将跳过其他验证函数 |
| Regexp | 使用正则表达式验证输入值 |
| URL | 验证URL |
| UUID | 验证UUID |
| AnyOf | 确保输入值在一组可能的值中 |
| NoneOf | 确保输入值不在一组可能的值中 |



## 4.3 把表单渲染成HTML

表单字段可以调用,调用后会渲染成HTML。

假设视图函数通过form参数把一个NameForm实例传入模板,在模板中可以生成一个简单的HTML表单

```html
<form method="POST">
<!--生成一个隐藏字段,供Flask-WTF的CRSF防护机制使用-->
{{ form.hidden_tag() }}
{{ form.name.label }}{{ form.name() }}
{{ form.submit() }}
</form>

为字段指定id或class属性,定义CSS样式

{{form.name(id='my-text-field')}}

templates/index.html:使用Flask-WTF和Flask-Bootstrap渲染表单

{% 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) }}--参数form为Flask-WTF表单对象,使用Bootstrap的默认样式渲染传入的表单
{% endblock %}

4.4 在视图函数中处理表单

完整代码

app.py

from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
from flask_bootstrap import Bootstrap

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


# 定义表单类
class NameForm(FlaskForm): # 含有文本字段name和提交按钮submit
name = StringField('What is your name?', validators=[DataRequired()])
submit = SubmitField('Submit')


@app.route('/', methods=['GET', 'POST'])
def index():
name = None
form = NameForm()
if form.validate_on_submit():
name = form.name.data
form.name.data = ''
return render_template('index.html', form=form, name=name)

base.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">
<div class="page-header">
{% block page_content %}{% endblock %}
</div>
</div>
{% endblock %}

index.html

{% 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 %}

image-20200428170326312

image-20200428170357498

image-20200428170414647

4.5 重定向和用户会话

修改app.py

from flask import Flask, render_template,session,redirect,url_for
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
from flask_bootstrap import Bootstrap

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


# 定义表单类
class NameForm(FlaskForm): # 含有文本字段name和提交按钮submit
name = StringField('What is your name?', validators=[DataRequired()])
submit = SubmitField('Submit')


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

4.6 闪现消息

app.py

from flask import Flask, render_template,session,redirect,url_for,flash
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
from flask_bootstrap import Bootstrap

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


# 定义表单类
class NameForm(FlaskForm): # 含有文本字段name和提交按钮submit
name = StringField('What is your name?', validators=[DataRequired()])
submit = SubmitField('Submit')


@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
old_name=session.get('name')
if old_name is not None and old_name!=form.name.data:
flash('looks like you have changed your name!')
session['name']=form.name.data
return redirect(url_for('index'))
return render_template('index.html',form=form,name=session.get('name'))

base.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">
{% for message in get_flashed_messages() %}
<div class="aler alert-warning">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ message }}
</div>
{% endfor %}
<div class="page-header">
{% block page_content %}{% endblock %}
</div>
</div>
{% endblock %}

index.html

{% 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 %}

部分内容借鉴:

  • 中国工信出版社集团 人民邮电出版社《Flask Web开发第二版》
Author: Michelle19l
Link: https://gitee.com/michelle19l/michelle19l/2020/04/24/Flask教程二/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶