# 表单提交
### flask-bootstrap
- 说明:在flask中如何使用bootstrap,可以通过此扩展进行解决。
- 安装:`pip install flask-bootstrap`
- 使用:
```python
from flask_bootstrap import Bootstrap
bootstrap = Bootstrap(app)
```
- 模板:
```html
{# 继承自bootstrap的基础模板 #}
{% extends 'bootstrap/base.html' %}
{% block title %}标题{% endblock %}
{% block content %}
<div class="container">
内容
</div>
{% endblock %}
```
- bootstrap基础模板中的block
| block | 说明 |
| ------- | ------------------ |
| doc | 整个HTML文档 |
| html | 整个html标签 |
| head | 整个head标签 |
| title | 整个title标签 |
| metas | 一组meta标签 |
| styles | 一组link标签(加载CSS文件) |
| body | 整个body标签 |
| navbar | 导航条 |
| content | 网页内容 |
| scripts | 一组script标签(加载JS文件) |
> 提示:当重写一个block后,发现原来的显示全没了,很可能是因为没有使用{{ super() }}。
### 项目基础模板定制
- 从bootstrap官网复制一个顺眼的导航条
- 将`container-fluid`改为`container`
- 改为反色导航条,将`navbar-default`改为`navbar-inverse`
- 设置为直角:`style="border-radius: 0px;"`
- 根据需要定制导航条上的显示内容
### 加载静态资源
- flask中静态资源默认存放在static目录下,因此目录结构如下:
```
project/
manage.py # 启动控制文件
templates/ # 模板文件目录
static/ # 静态资源目录
img/
css/
js/
favicon.ico
```
- 加载静态资源
```html
{# 基础模板中,加载网站收藏夹小图标 #}
{% block head %}
{{ super() }}
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}" />
{% endblock %}
{# 加载图片资源 #}
<img src="{{ url_for('static', filename='img/gyy.jpg') }}" />
{# 加载CSS文件 #}
{% block styles %}
{{ super() }}
<link type="text/css" href="{{ url_for('static', filename='css/common.css') }}" rel="stylesheet" />
{% endblock %}
{# 加载JS文件 #}
{% block scripts %}
{{ super() }}
<script type="text/javascript" src="{{ url_for('static', filename='js/common.js') }}"></script>
{% endblock %}
```
### 原生表单
- 准备模板文件`login.html`:
```html
<form method="post" action="/check/">
用户名:<input name="username" /><br />
<input type="submit" />
</form>
```
- 添加视图函数,并渲染模板文件:
```python
@app.route('/login/')
def login():
return render_template('login.html')
```
- 添加检验视图函数:
```python
@app.route('/check/', methods=['POST'])
def check():
return '登录成功'
```
- 一个路由接收两种请求:
```python
@app.route('/login/', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
else:
# request.form存放了所有的POST请求数据
# return request.form.get('username')
# request.values存放了所有GET/POST请求数据
return request.values.get('username')
```
### flask-wtf
- 说明:表单处理的扩展库,提供了CSRF、字段校验等功能,使用非常方便
- 安装:`pip install flask-wtf`
- 使用:
- 创建表单类
```python
# 导入表单基类
from flask_wtf import FlaskForm
# 导入相关字段
from wtforms import StringField, SubmitField
# 导入相关验证器
from wtforms.validators import Length
# 创建表单类
class NameForm(FlaskForm):
name = StringField('用户名', validators=[Length(3, 10, message='用户名长度必须在3~10个字符之间')])
submit = SubmitField('提交')
```
- 添加视图函数,创建表单对象,并渲染模板文件:
```python
@app.route('/', methods=['GET', 'POST'])
def index():
# 创建表单对象
form = NameForm()
# 判断是否是有效的提交
if form.validate_on_submit():
# 提取表单数据
return form.name.data
return render_template('form.html', form=form)
```
- 原生渲染表单
```html
<form method="post">
{# CSRF字段 #}
{{ form.hidden_tag() }}
{{ form.name.label() }}{{ form.name(id='xxx', class='yyy') }}
{% for e in form.name.errors %}
<div>{{ e }}</div>
{% endfor %}
{{ form.submit() }}
</form>
```
- 使用bootstrap方式进行快速渲染
```html
{# 继承自bootstrap基础模板 #}
{% extends 'bootstrap/base.html' %}
{# 导入快速渲染的宏 #}
{% from 'bootstrap/wtf.html' import quick_form %}
{% block content %}
<div class="container">
{# 在合适位置快速渲染表单 #}
{{ quick_form(form) }}
</div>
{% endblock %}
```
- POST重定向到GET:因为浏览器会记录最后的请求状态,点击刷新时POST请求会有问题。
```python
@app.route('/', methods=['GET', 'POST'])
def index():
# 创建表单对象
form = NameForm()
# 判断是否是有效的提交
if form.validate_on_submit():
# 提取表单数据
session['name'] = form.name.data
return redirect(url_for('index'))
name = session.get('name')
return render_template('form2.html', form=form, name=name)
```
- 常见字段类型
| 字段类型 | 说明 |
| ------------- | ------------------- |
| StringField | 普通文本字段 |
| Submit | 提交按钮 |
| PasswordField | 密文字段 |
| HiddenField | 隐藏字段 |
| RadioField | 单选框 |
| BooleanField | 复选框 |
| FileField | 文件上传 |
| SelectField | 下拉框 |
| TextAreaField | 文本域 |
| IntegerField | 文本字段,值为整数 |
| FloatField | 文本字段,值为浮点数 |
| DateField | datetime.date类型 |
| DateTimeField | datetime.datetime类型 |
- 常见验证器
| 验证器 | 说明 |
| ------------ | ------------------- |
| Length | 规定字符长度 |
| DataRequired | 确保字段有值(提示信息与所写的不一致) |
| Email | 邮箱地址 |
| IPAddress | IP地址 |
| NumberRange | 数值的范围 |
| URL | 统一资源定位符 |
| EqualTo | 验证两个字段的一致性 |
| Regexp | 正则验证 |
- 自定义验证函数
```python
from wtforms.validators import ValidationError
class NameForm(FlaskForm):
。。。
def validate_name(self, field):
if len(field.data) < 6:
raise ValidationError('用户名不能少于6个字符')
```
> 总结:写字段验证函数,就是写一个'validate_字段名'的函数
### 消息闪烁
- 说明:
当用户发出请求后,状态发生了改变,需要系统给出警告、提示等信息时,通常是弹出一条消息,指示用户下一步的操作,用户可以手动关闭提示消息。而且整个的过程不会影响页面原有的显示。
- 使用:
- 在需要弹出消息时,使用`flash`函数保存闪烁消息
```python
@app.route('/', methods=['GET', 'POST'])
def index():
# 创建表单对象
form = NameForm()
# 判断是否是有效的提交
if form.validate_on_submit():
last_name = session.get('name')
if last_name and form.name.data != last_name:
flash('大哥,又换签名了!')
# 提取表单数据
session['name'] = form.name.data
return redirect(url_for('index'))
name = session.get('name')
return render_template('form2.html', form=form, name=name)
```
- 在模板文件中显示闪烁消息时,可以通过函数`get_flashed_messages`获取
- 从bootstrap上粘贴一个可消失的警告框
```html
{% for message in get_flashed_messages() %}
<div class="alert alert-warning alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span>
</button>
{{ message }}
</div>
{% endfor %}
```
### flask-moment
- 说明:专门负责时间本地化显示的扩展库,使用非常方便
- 安装:`pip install flask-moment`
- 使用:
- python代码
```python
from flask_moment import Moment
moment = Moment(app)
@app.route('/mom/')
def mom():
from datetime import datetime, timedelta
current_time = datetime.utcnow() + timedelta(seconds=-60)
return render_template('mom.html', current_time=current_time)
```
- 模板文件
```html
{# 加载jQuery #}
{{ moment.include_jquery() }}
{# 加载moment.js #}
{{ moment.include_moment() }}
{# 设置语言 #}
{{ moment.locale('zh-CN') }}
{# 简单的格式化时间显示 #}
<div>时间:{{ moment(current_time).format('LLLL') }}</div>
<div>时间:{{ moment(current_time).format('LLL') }}</div>
<div>时间:{{ moment(current_time).format('LL') }}</div>
<div>时间:{{ moment(current_time).format('L') }}</div>
{# 自定义格式化显示 #}
<div>自定义:{{ moment(current_time).format('YYYY-MM-DD HH:mm:ss') }}</div>
{# 显示时间差 #}
<div>发表于:{{ moment(current_time).fromNow() }}</div>
```
### 练习:
- 完成用户的注册登录功能