1. 认识 Flask
Flask 是一个微型 Web 框架,依赖于 jinjia2 模板系统和 Werkzeug WSGI(本质为 Socket 服务端) 服务,默认情况不支持数据库抽象层、表单验证,如果要使用可以进行拓展。
Flask 常用扩展包:
- Flask-SQLalchemy:操作数据库;
- Flask-migrate:管理迁移数据库;
- Flask-Mail:邮件;
- Flask-WTF:表单;
- Flask-script:插入脚本;
- Flask-Login:认证用户状态;
- Flask-RESTful:开发REST API的工具;
- Flask-Bootstrap:集成前端Twitter Bootstrap框架;
- Flask-Moment:本地化日期和时间;
安装:
pip3 install flask
Werkzeug:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from werkzeug.wrappers import Request, Response
@Request.application
def hello(request):
return Response('Hello World!')
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 4000, hello)
尝鲜:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "Index Page"
if __name__ == "__main__":
app.run()
官网地址:
2. 路由系统
2.1 五种常用路由
Flask 常用五种路由:
# 前四种可以接收变量
@app.route('/user/<username>') # /user/rose
@app.route('/post/<int:post_id>') # /post/1
@app.route('/post/<float:post_id>') # /post/1.2
@app.route('/post/<path:subpath>') # /post/static/css/xx.css
@app.route('/login', methods=['GET', 'POST'])
示例:
@app.route('/user/<username>')
def show_user_profile(username):
return 'User %s' % username
以上五种路由基于以下对应关系:
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
让 Flask 支持正则:https://segmentfault.com/q/1010000000125259
。
2.2 反解 URL
url_for()
方法可以根据视图函数名,反解出 url,类似于 Django 的 reverse()
方法:
# 接收视图函数名字,返回所对应的绝对 URL
url_for('index', _external=True)
from flask import Flask, redirect, url_for
app = Flask(__name__)
@app.route("/")
def index():
return "Index Page"
@app.route("/hello")
def hello():
return "Hello World!"
@app.route('/user/<username>')
def profile(username):
return username
with app.test_request_context():
print(url_for('hello'))
print(url_for('profile', username='rose'))
print(url_for('index'))
if __name__ == "__main__":
app.run()
test_request_context()
告诉 Flask
,即使我们使用Python shell
,它也会像处理请求一样行事。
运行结果如下:
/hello
/user/rose
/
2.3 正则匹配
from flask import Flask
from werkzeug.routing import BaseConverter
class Regex_url(BaseConverter):
def __init__(self,url_map,*args):
super(Regex_url,self).__init__(url_map)
self.regex = args[0]
app = Flask(__name__)
app.url_map.converters['re'] = Regex_url
@app.route('/user/<re("[a-z]{3}"):id>')
def hello_itcast(id):
return 'hello %s' % id
if __name__ == '__main__':
app.run()
3. 模板系统
3.1 静态文件和模板路径设置
from flask import Flask, redirect, url_for, render_template
app = Flask(__name__) # 静态文件和模板依赖这句
@app.route('/hello/')
def hello():
return render_template('t1.html')
if __name__ == '__main__':
app.run()
静态文件语法:
url_for('static', filename='style.css')
静态文件目录 static
在项目根目录,与程序同级(需要自己创建),Flask 默认回会去 static
中查找,你可以在里面新建子文件夹:
<img src="{{ url_for('static', filename='imgs/1.png') }}">
模板路径设置
Flask 会在 templates
文件夹里寻找模板。所以,如果你的应用是个模块,这个文件夹应该与模块同级;如果它是一个包,那么这个文件夹作为包的子目录:
情况 1: 模块:
/application.py
/templates
/hello.html
情况 2: 包:
/application
/__init__.py
/templates
/hello.html
3.2 模板语言及自定义模板
Flask 使用的是 Jinja2模板,与 Django 一样,因此语法也没什么差别。
自定义模板
在模板中使用 Python 函数:
app.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask, render_template
app = Flask(__name__)
def func():
return '<h1>Flask 自定义模板</h1>'
@app.route('/index', methods=['GET', 'POST'])
def index():
return render_template('index.html', func=func)
if __name__ == '__main__':
app.run()
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ func() | safe }} <!-- safe 不转义 -->
</body>
</html>
3.3 过滤器
语法:
变量名 | 过滤器
字符串过滤器:
# 禁用转义 safe
<p>{{ '<em>hello</em>' | safe }}</p>
# capitalize:把变量值的首字母转成大写,其余字母转小写;
<p>{{ 'hello' | capitalize }}</p>
# lower:把值转成小写;
<p>{{ 'HELLO' | lower }}</p>
# upper:把值转成大写;
<p>{{ 'hello' | upper }}</p>
# title:把值中的每个单词的首字母都转成大写;
<p>{{ 'hello' | title }}</p>
#trim:把值的首尾空格去掉;
<p>{{ ' hello world ' | trim }}</p>
# reverse:字符串反转;
<p>{{ 'olleh' | reverse }}</p>
# format:格式化输出;
<p>{{ '%s is %d' | format('name',17) }}</p>
# striptags:渲染之前把值中所有的HTML标签都删掉;
<p>{{ '<em>hello</em>' | striptags }}</p>
列表操作:
# first:取第一个元素
<p>{{ [1,2,3,4,5,6] | first }}</p>
# last:取最后一个元素
<p>{{ [1,2,3,4,5,6] | last }}</p>
# length:获取列表长度
<p>{{ [1,2,3,4,5,6] | length }}</p>
# sum:列表求和
<p>{{ [1,2,3,4,5,6] | sum }}</p>
# sort:列表排序
<p>{{ [6,2,3,1,5,4] | sort }}</p>
自定义过滤器
过滤器的本质是函数,两种实现方式:一种是通过Flask应用对象的add_template_filter
方法。还可以通过装饰器来实现自定义过滤器,与内置过滤器重名则覆盖:
1、add_template_filter
,第一个参数是函数名,第二个参数是自定义的过滤器名称
def filter_double_sort(ls):
return ls[::2]
app.add_template_filter(filter_double_sort,'double_2')
2、装饰器来实现自定义过滤器,参数为过滤器名字:
@app.template_filter('db3')
def filter_double_sort(ls):
return ls[::-3]
app.py
from flask import Flask, render_template
app = Flask(__name__)
def filter_double_sort(ls):
return ls[::2]
app.add_template_filter(filter_double_sort, 'double_2')
@app.route('/')
def index():
data_list = [1, 2, 5, 8]
return render_template('index4.html', data_list=data_list)
if __name__ == "__main__":
app.run()
index4.html
<p>{{ data_list | double_2}}</p>
3.5 宏
类似于 python
中的函数,宏的作用就是在模板中重复利用代码,避免代码冗余。
定义不带参数的宏:
<!-- 定义宏 -->
{% macro input() %}
<input type="text" name="username" value="" size="30"/>
{% endmacro %}
<!-- 调用宏 -->
{{ input() }}
定义带参数的宏:
{% macro input(name,value='',type='text',size=20) %}
<input type="{{ type }}"
name="{{ name }}"
value="{{ value }}"
size="{{ size }}"/>
{% endmacro %}
<!-- 调用宏,并传递参数 -->
{{ input(value='name',type='password',size=40)}}
还可以把宏单独抽取出来,封装成 html
文件,其它模板中导入使用文件名可以自定义 macro.html
:
{% macro function() %}
<input type="text" name="username" placeholde="Username">
<input type="password" name="password" placeholde="Password">
<input type="submit">
{% endmacro %}
在其它模板文件中先导入,再调用:
{% import 'macro.html' as func %}
{% func.function() %}
宏、继承(extend)和包含(include)区别:
- 宏(Macro)、继承(Block)、包含(include)均能实现代码的复用
- 继承(Block):本质是代码替换,一般用来实现多个页面中重复不变的区域
- 宏(Macro):功能类似函数,可以传入参数,需要定义、调用
- 包含(include):是直接将目标模板文件整个渲染出来
3.4 特殊变量和方法
- config 对象: Flask的config对象,也就是 app.config 对象,
{{ config.SQLALCHEMY_DATABASE_URI }}
-
get_flashed_messages
方法:应用在模板中一般用于获取错误信息,返回之前在Flask中通过 flash() 传入的信息列表。把字符串对象表示的消息加入到一个消息队列中,然后通过调用 get_flashed_messages() 方法取出。
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %}
4. 公共组件
4.1 请求和响应
1、请求 request
浏览器发送过来的 HTTP 请求,被 flask 封装在 request中(werkzeug.wrappers.BaseRequest)
中,提供了一下字段或方法以供使用:
request.method # 请求方法
request.args # 获取GET请求参数
request.form # 获取POST请求参数
request.values
request.files # 获取上传文件
request.cookies # cookies
request.headers # 请求头
request.path
request.full_path
request.script_root
request.url # 请求 URL
request.base_url # 获取请求路径
request.url_root
request.host_url
request.host # 获取ip和端口
2、响应 response
1、模板渲染、重定向
模板渲染:render_template
、重定向:redirect
、url_for
from flask import Flask, render_template, request, redirect, url_for
app = Flask(__name__)
@app.route('/index/', method=['GET', 'POST'])
def index():
return render_template('index.html')
# redirect(url_for('login')) # 重定向
2、错误信息
from flask import Flask, abort, render_template
app = Flask(__name__)
@app.route('/index/', methods=['GET', 'POST'])
def index():
abort(404, 'Page Not Found!') # 返回一个 Page Not Found! 的页面
if __name__ == '__main__':
app.run()
3、make_reponse
make_response
类似于 Django 的 HTTPResponse
,用于返回字符串,下面是几个比较常见的用法:
返回内容、页面:
from flask import Flask, abort, render_template, make_response
app = Flask(__name__)
def func():
return '<h1>Flask 自定义模板</h1>'
@app.route('/index/', methods=['GET', 'POST'])
def index():
# return make_response('make response') # 返回内容
response = make_response(render_template('index.html', func=func)) # 返回页面
# return response, 200, # 返回内容和状态码
response
是 flask.wrappers.Response
类型,同时 make_resposne
还可以用来设置响应头、设置 cookie 等:
# response.delete_cookie
# response.set_cookie
# response.headers['X-Something'] = 'A value'
@app.route('/index/', methods=['GET', 'POST'])
def index():
response = make_response(render_template('index.html', func=func))
response.headers['hahaha'] = 'hello world'
return response
4.2 自定义错误页面
如果在浏览器中输入不可用的路由,那么就会显示一个 404 状态的错误页面,这个页面太过简陋、平庸,并不是我们所希望的。好在 Flask 允许我们基于模板自定义错误页面,需要用到模块:flask-bootstrap
。
安装:
pip3 install flask-bootstrap
下面来示范两个最常见的错误:404 和 500:
1、app.py
初始化 bootstrap
,定义两个函数,用于处理 404 和 500 错误页面:
from flask import Flask, abort, render_template
from flask_bootstrap import Bootstrap
app = Flask(__name__)
bootstrap = Bootstrap(app) # 初始化,千万别忘记
@app.route('/index/', methods=['GET', 'POST'])
def index():
return "OK"
@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
if __name__ == '__main__':
app.run()
2、在模板中定义一个母版 templates/errors/base.html
,编辑如下:
{%extends "bootstrap/base.html"%}
{%block title %}Flask{% 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 %}
3、下面再编写 404.html
和 500.html
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 %}
500.html
{% extends "base.html" %}
{% block title %}Flasky - internal server error{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>internal server error</h1>
</div>
{% endblock %}
现在我们来访问一个不存在的页面看看:http://127.0.0.1:5000/main/
似乎好看了点,当然你还可以定制更为高端一点。
4.3 Session
Session 依赖于 Cookies ,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。
- 设置:
session['username'] = 'xxx'
- 删除:
session.pop('username', None)
- 获取:
session.get('username')
设置获取 Cookie
from flask import Flask,make_response
@app.route('/cookie')
def set_cookie():
resp = make_response('this is to set cookie')
resp.set_cookie('username', 'itcast')
return resp
from flask import Flask,request
#获取cookie
@app.route('/request')
def resp_cookie():
resp = request.cookies.get('username')
return resp
示例:利用 Session 验证用户登录
from flask import Flask, session, redirect, url_for, escape, request
app = Flask(__name__)
@app.route('/')
def index():
if 'username' in session:
return '欢迎回来: %s' % escape(session['username'])
return '你没有登录' # 也可以重定向到登录页面
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
session['username'] = request.form['username']
return redirect(url_for('index'))
return '''
<form action="" method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
'''
@app.route('/logout')
def logout():
# 从 session 移除 username
session.pop('username', None)
return redirect(url_for('index'))
# 设置密钥
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
if __name__ == '__main__':
app.run()
4.3 message
message 是一个基于 Session 实现的用于保存数据的集合,其特点是:使用一次就删除
app.py
from flask import Flask, flash, redirect, render_template, request
app = Flask(__name__)
app.secret_key = 'some_secret'
@app.route('/')
def index1():
return render_template('index2.html')
@app.route('/set')
def index2():
v = request.args.get('p')
flash(v) # 将消息给下一个请求,并将其删除,模板必须调用 get_flashed_messages() 函数
return 'ok'
if __name__ == "__main__":
app.run()
index2.html
模板中必须使用 get_flashed_messages()
才能获取到 message
传递的信息:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>messages</title>
</head>
<body>
{% with messages = get_flashed_messages() %} <!-- with xxx:取别名 -->
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
</body>
</html>
首先请求:http://127.0.0.1:5000/set?p=123
,index()
函数获取 p 的值,并将其传递给下一个请求(传递后即将其删除)。
第二个请求访问:http://127.0.0.1:5000/
,从而获得上一次请求传递的信息:
4.4 请求钩子
所谓请求钩子即是用来处理请求前、后,一些特定事情的功能实现。如:请求前连接数据库、请求后指定数据交互格式等。Flask 中请求钩子以装饰器形式实现:
Flask 中请求钩子:
- before_first_request:在处理第一个请求前运行,比如:数据库的连接,初始化操作
- before_request:在每次请求前运行。
- after_request:如果没有未处理的异常抛出,在每次请求后运行。
- teardown_request:在每次请求后运行,即使有未处理的异常抛出。
示例:基于 before_request
实现用户登录认证
相比较装饰器而已有如下优势:
- 装饰器实现用户登录认证,需要给每个需要认证的视图函数添加装饰器
-
before_request
,只需一个即可。
from flask import Flask, render_template, request, redirect, session, url_for
app = Flask(__name__)
app.secret_key = "123$456"
# 基于flask里请求扩展来做
@app.before_request
def process_request(*args, **kwargs):
"""不管访问哪个视图函数,都会实现执行这个函数,也就意味着每次都会检查是否登录"""
# 访问/login的时候还没有登录,就会一直重定向到登录页,所以就要设置个白名单,如果请求地址是/login,就返回None
if request.path == "/login":
return None
# 1.登录验证功能
user = session.get('user_info')
# 2.如果登录信息正常,什么都不做,程序继续其他执行
if user:
return None
# 3.如果登录验证不通过,就重定向到登录页面
return redirect("/login")
@app.route("/index", methods=['GET'])
def index():
name = session.get('user_info', None)
return 'Welcome, %s' % name
@app.route("/login", methods=['GET', 'POST'])
def login():
if request.method == "GET":
return render_template("login.html")
else:
user = request.form.get("username")
pwd = request.form.get("password")
if user == "rose" and pwd == "123456":
session['user_info'] = user
return redirect("/index")
return render_template("login.html", **{"error": "用户名和密码错误"})
if __name__ == "__main__":
app.run()
也可以有多个请求钩子,但是要注意的是当有多个请求中间件时,执行顺序是不同的:
-
before_request
:从上到下按照顺序执行 -
after_request
:从下到上的顺序执行 - 当有多个
before_request
时,若第一个有return
语句,那么后面的before_request
将被拦截不被执行,但是after_request
会继续执行
4.5 中间件
Flask 程序在运行过程中会事先加载 wsgi_app
,基于此我们可以拓展做些别的事情:
from flask import Flask
app = Flask(__name__)
@app.route("/login", methods=['GET', 'POST'])
def index():
pass
class Md(object):
def __init__(self, old_wsgi_app):
self.old_wsgi_app = old_wsgi_app
def __call__(self, environ, start_response):
print("开始之前")
ret = self.old_wsgi_app(environ, start_response)
print("结束之后")
return ret
if __name__ =="__main__":
app.wsgi_app = Md(app.wsgi_app) # 相当于把wsgi_app给更新了
app.run()
5. 表单
5.1 表单拓展 WTForms
表单由三个部分组成:表单标签、表单域、表单按钮。Flask 通过Flask-WTF 实现表单功能。它封装了WTForms,可以生成表单、以及验证表单数据。
安装:
pip3 install Flask-WTF
pip3 install flask-moment
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 常用验证函数:
验证函数 | 说明 |
---|---|
DataRequired | 确保字段中有数据 |
AEqualTo | 比较两个字段的值,常用于比较两次密码输入 |
Length | 验证输入的字符串长度 |
NumberRange | 验证输入的值在数字范围内 |
URL | 验证URL |
AnyOf | 验证输入值在可选列表中 |
NoneOf | 验证输入值不在可选列表中 |
跨站请求伪造保护 csrf
设置一个密匙,生成加密令牌,再用令牌验证请求表单中数据真伪:
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string' # 密匙随便取
# 如果你设置的模板中存在表单,你只需要在表单中添加如下
<form method="post" action="/">
{{ form.csrf_token }}
</form>
# 如果没有模板中没有表单,你仍然需要一个 CSRF 令牌
<form method="post" action="/">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
</form>
简单示例
1、定义一个表单类:
from flask_wtf import FlaskForm
#导入自定义表单需要的字段
from wtforms import SubmitField, StringField, PasswordField
#导入wtf扩展提供的表单验证器
from wtforms.validators import DataRequired, EqualTo
# validators 不能为空
class NameForm(FlaskForm):
name = StringField(label='用户名', validators=[Required()])
submit = SubmitField('提交')
2、在模板中使用表单:
<h1>Hello, {% if name %}{{ name }} {% else %} Stranger {% endif %}!</h1>
<form method='post'>
{{ form.hidden_tag() }}
{{ form.name.label }} {{form.name() }}
{{ form.submit() }}
# 传入 HTML 属性
{{ form.name.label }} {{form.name(id='id_name') }}
</form>
总是自己传属性太麻烦,而且表单样式过于丑陋。可以用 flask-bootstrap
,它是对 bootstrap
进行了简单封装,安装好之后可以直接调用:
<!--上面表单可用下面方式一次渲染-->
{% import 'bootstrap/wtf.html' as wtf %}
{{ wtf.quick_form(form) }}
from flask_bootstrap import Bootstrap
app = Flask(__name__)
bootstrap = Bootstrap(app)
wtf.quick_form(form)
函数参数为 Flask-WTF
表单对象
<!-- 条件结果为 True,渲染 if else 中文字,否则渲染 else endif 中语句 -->
<h1>Hello, {% if name %}{{ name }} {% else %} Stranger {% endif %}!</h1>
5.2 基于 WTForms 实现用户登录验证
实现方式:继承+WTForms +Flask-Bootstrap
app.py
from flask import Flask, render_template, redirect, url_for, session, request,flash
from flask_bootstrap import Bootstrap
from flask_moment import Moment
#导入wtf扩展的表单类
from flask_wtf import FlaskForm
#导入自定义表单需要的字段
from wtforms import SubmitField, StringField, PasswordField
#导入wtf扩展提供的表单验证器
from wtforms.validators import DataRequired, EqualTo
app = Flask(__name__)
app.config['SECRET_KEY'] = '1'
bootstrap = Bootstrap(app) # 使用 bootstrap 渲染表单
moment = Moment(app) # 本地化时间
#自定义表单类,文本字段、密码字段、提交按钮
class Login(FlaskForm):
user = StringField(label=u'用户:', validators=[DataRequired()])
pwd1 = PasswordField(label=u'密码', validators=[DataRequired()])
pwd2 = PasswordField(label=u'确认密码', validators=[DataRequired()])
submit = SubmitField(u'提交')
@app.route('/', methods=['GET','POST'])
def index():
return render_template('index.html')
#定义根路由视图函数,生成表单对象,获取表单数据,进行表单数据验证
@app.route('/login', methods=['GET','POST'])
def login():
form = Login()
if form.validate_on_submit():
name = form.user.data
pwd1 = form.pwd1.data
pwd2 = form.pwd2.data
print(name, pwd1, pwd2)
if name == 'rose' and (pwd1 == pwd2 and pwd2 == '123'):
return redirect(url_for('index'))
else:
flash(u'用户名或密码错误!')
return render_template('login.html', form=form)
if __name__ == '__main__':
app.run()
include/base.html
{% extends "bootstrap/base.html" %}
{% block title %}Flask WTF{% endblock %}
{% block head %}
{{ super() }}
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
{% 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="/">Flask WTF</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">
<!-- get_flashed_messages() 显示错误信息 -->
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
{% endfor %}
{% block page_content %}{% endblock %}
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{% endblock %}
login.html
继承 base.html
{% extends "include/base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flask WTF{% endblock %}
{% block page_content %}
<div class="page-header">
</div>
{{ wtf.quick_form(form) }}
{% endblock %}
将用户个人信息保存到会话中:
app.py
from flask import Flask, render_template, redirect, url_for, session, request,flash
def login('/login', methods=['GET', 'POST']):
...
# 会话中原有值
old_name = session.get('name')
if old_name is not None and old_name != form.name.data:
flash('用户名错误!')
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('login.html', form=form, name=session.get('name'))
login.html
<div class="page-header">
<h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1>
</div>