Flask 入门(第一篇)

时间:2022-11-25 08:09:25

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、重定向:redirecturl_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()

Flask 入门(第一篇)

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, # 返回内容和状态码

responseflask.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

Flask 入门(第一篇)

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.html500.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/

Flask 入门(第一篇)

似乎好看了点,当然你还可以定制更为高端一点。

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=123index() 函数获取 p 的值,并将其传递给下一个请求(传递后即将其删除)。

第二个请求访问:http://127.0.0.1:5000/,从而获得上一次请求传递的信息:

Flask 入门(第一篇)

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 会继续执行

Flask 入门(第一篇)

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">&times;</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>

Flask 入门(第一篇)