Flask 中集成了 模板引擎 jinja2
app.py
from flask import Flask,render_template
user = {
"username":"Wu han",
"bio":"A boy loves movies and music"
}
movies = [
{'name': 'My Neighbor Totoro', 'year': '1988'},
{'name': 'Three Colours trilogy', 'year': '1993'},
{'name': 'Forrest Gump', 'year': '1994'},
{'name': 'Perfect Blue', 'year': '1997'},
{'name': 'The Matrix', 'year': '1999'},
{'name': 'Memento', 'year': '2000'},
{'name': 'The Bucket list', 'year': '2007'},
{'name': 'Black Swan', 'year': '2010'},
{'name': 'Gone Girl', 'year': '2014'},
{'name': 'CoCo', 'year': '2017'}
]
app = Flask(__name__)
@app.route('/')
@app.route('/hello')
def index():
return render_template('watchlist.html', user=user, movies=movies)
templates/watchlist.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{user.username}}</title> # 调用获取变量属性
</head>
<body>
<a href="{{url_for('index')}}">← Return</a>
<h2>{{user.username}}</h2>
{% if user.bio %} # if循环
<i> {{user.bio}}</i>
{% endif %}
{# 下面是电影清单(这是注释) #}
<h5>{{ user.username }}'s Watchlist ({{ movies|length }})</h5>
<ul>
{% for movie in movies %} # for 循环
<li>{{ movie.name }} - {{ movie.year }}</li>
{% endfor %}
</ul>
</body>
</html>
- 语句
{% ... %} : if 、for 循环等
{% if user.bio %} # if循环
<i> {{user.bio}}</i>
{% else %}
<i> This user has not provided a bio.</i>
{% endif %}
{% for movie in movies %} # for 循环
<li>{{ movie.name }} - {{ movie.year }}</li>
{% endfor %}
- 表达式
{{ ... }} : 字符串,变量,函数调用等
- 注释
{# #}
| 变量名 | 说明 |
|---|---|
| loop.index | 当前迭代数(从1开始计数) |
| loop.index() | 当前迭代数(从0开始计数) |
| loop.revindex | 当前反向迭代数(从1开始计数) |
| loop.revindex() | 当前反向迭代数(从0开始计数) |
| loop.first | 如果是第一个元素,则为True |
| loop.last | 如果是最后一个元素,则为True |
| loop.previtem | 上一个迭代得条目 |
| loop.nextitem | 下一个迭代得条目 |
| loop.length | 序列包含得元素数量 |
渲染模板
render_template() 函数来渲染
@app.route('/')
@app.route('/hello')
def index():
return render_template('watchlist.html', user=user, movies=movies)
render_template_string() 函数用来渲染模板字符串
定义模板上下文
- 在模板中定义变量,使用
set标签
{% set navigation = [('/','Home'), ('/about','About')] %}
{% set navigation %}
<li>
<a href="/">Home</a>
<li>
<a href="/about">About</a>
{% endset %}
标准模板全局变量
config:当前得配置对象request:当前的请求对象,在已激活得请求环境下可用session:当前得回话对象,在已激活得请求环境下可用g:与请求绑定得全局变量,在已激活得请求环境下可用
自定义上下文
使用app.context_processor 装饰器来注册模板上下文处理函数。模板上下文函数需要返回一个包含变量键值对得字典。
方式1:
@app.context_processor
def inject_foo():
foo = "I am foo."
return dict(foo=foo) # 等同于return {'foo':foo}
方式2:
def inject_foo():
foo = "I am foo."
return dict(foo=foo)
app.context_processor(inject_foo)
方式3:
app.context_processor(lambda : dict(foo="I am foo."))
↑:当我们调用 render_template函数渲染得任意一个模板时候,所有使用app.context_processor装饰器注册得模板上下文处理函数都会被执行,这些函数得返回值会添加到模板中,因为我们可以在 模板中直接使用foo变量。
全局对象
- 内置全局函数(常用)
range([start,]stop[,step]):和Python中的range()用法相同lipsum(n=5, html=True,min=20,max=100):生成随机文本(lorem ipsum),可以在测试时用来填充页面,默认生成5端HTML文本,每段包含20~100个单词dict(**items):和Python中dict()用法相同- Flask内置模板全局函数
url_for():用于生成URL得函数
<a href='{{ url_for('index') }}'>← Return</a>
get_flashed_messages:用于获取flask消息得函数- 自定义全局函数
@app.template_global([name=])
@app.template_global()
def bar():
return "I am bar."
app.add_template_global(your_global_function)
过滤器
标题过滤器
{{ name|title }} # 将name得字符串转换为标题格式得 跟 python中 name.title()一样
长度过滤器
{{ movies|length }} # 获取movies列表得长度
标签过滤器
{% filter upper %} # 将下面这段文字全部转换成大写
This text becomes uppercase.
{% endfilter %}
过滤器可以叠加使用
<h1>Hello, {{ name|default('陌生人')|title }}!</h1>
内置过滤器:
| 过滤器 | 说明 |
|---|---|
| default(value, default_value=u"", boolean=False) | 设置默认值,默认值作为参数传入,别名为d |
| escape(s) | 转义HTML文本,别名为e |
| first(seq) | 返回序列得第一个元素 |
| last(seq) | 返回序列得最后一个元素 |
| length(object) | 返回变量得长度 |
| random(seq) | 返回序列初中的随机元素 |
| safe(value) | 将变量值标记为安全,避免转义 |
| trim(value) | 消除变量值前后的空格 |
| max(value, case_sensitive=False, attribute=None) | 返回序列中的最大值 |
| min(value, case_sensitive=False, attribute=None) | 返回序列中最小值 | | unique(value, case_sensitive=False, attribute=None) | 返回序列中的不重复的值 | | striptage(value) | 消除变量值内的HTML标签 | | urlize(value, trim_url_limit=None, onfollow=False, target=None,rel=None) | 将url文本转换为可单击的HTML链接| | wordcount(s) | 计算单词数量| | tojson(value, indent=None) | 将变量值转换为JSON格式| | truncate(s, length=255, killwords=False, end="...", leeway=None) | 截断字符串,常用语显示文章摘要,length参数设置截断的长度,killwords参数设置是否截断单词,end参数设置结尾符号 |
注:我们介绍了XSS攻击的主要防范措施,其中最主要的是对用户输入的文本进行转义。根据Flask的设置,Jinja2会自动对模板中的变量进行转义,所以我们不用手动使用escape过滤器或调用escape()函数对变量进行转义。
将文本标记为安全的方法:
因为jinja2会对文本默认进行转义,所以如果不想进行转义得话 那么需要一个操作
方式1:
{{ sanitieze_text | safe }}
方式2:
from flask import Markup
@app.route('/hello')
def hello():
text = Markup('<h1>Hello, Flask!</h1>')
return render_template('index.html', text = text)
自定义过滤器:
app.template_filter()装饰器
from flask import Markup
@app.template_filter()
def musical(s):
return s + Markup(' ♫')
{{ name|musical }}
测试器:
测试器(Test)是一些用来测试变量或者本次奥大师,返回布尔值(True或者False)得特殊函数。
{% if age is number %}
{{ age * 365}}
{% else %}
无效得数字
{% endif %}
内置测试器:
| 测试器 | 说明 |
|---|---|
| callacble(object) | 判断对象是否可被调用 |
| defined(value) | 判断变量是否已定义 |
| undefined(value) | 判断变量是否未定义 |
| none(value) | 判断变量是为None |
| number(value) | 判断变量是否是数字 |
| string(value) | 判断变量是否是字符串 |
| sequence(value) | 判断变量是否是序列,比如字符串,列表,元祖 |
| iterable(value) | 判断变量是否可迭代 |
| mapping(value) | 判断变量是否是匹配对象,比如字典 |
| sameas(value, other) | 判断变量与other是否指向相同的内存地址 |
用法:
{%if foo is sameas(bar) %}
↑↓ 效果一样
{%if foo is sameas bar %}
自定义测试器
app.template_test()装饰器
@app.template_test()
def baz(n):
if n == "baz":
return True
return False
模板环境对象
在程序中,可以使用app.jinja_env更改jinja2设置。
app = Flask(__name__)
# 修改定界符
app.jinja_env.variable_start_string = '[['
app.jinjaenv.variable_end_string = ']]'
添加自定义全局对象
def bar():
return 'I am bar.'
foo = 'I am foo.'
app.jinja_env.globals['bar'] = bar
app.jinja_env.globals['foo'] = foo
添加自定义过滤器
def smiling(s):
return s + ' :)'
app.jinja_env.filters['smiling'] = smiling
添加自定义测试器
def baz(n):
if n == 'baz':
return True
return False
app.jinja_env.tests['baz'] = baz
模板结构组织
局部模板
{% include '_bannner.html' %} # 局部模板命名一般以 ‘_’ 开始
宏
macros.html / _macros.html # 最好把宏单独存放,方便管理。
{% macros qux(amount=1) %}
{% if amount == 1 %}
I am qux.
{% elif amount > 1 %}
We are quxs.
{% endif %}
{% endfor %}
引用:
{% from 'macros.html' import qux %}
{{ qux(amount=5) }}
模板继承
{% block head %} {% endblock head %}
{% block styles %}
{% block content %}
{% block footer %}
{% block scripts %}
用法:
{% extends "greeter.html"%}
{% block times %}
# 这是子模板的内容
{% endblock times %}
应用定义好的块
# 引用定义好的块
{{ self.块名() }}
============================
----base.html
{% bclock title %}
<h1> This is base.html </h1>
{% endblock %}
----page.html
{% bclock body%}
{{ self.title() }}
<h1> This is body </h1>
{% endblock %}
=====
# 输出:
This is base.html
This is body
super() 用法
{% bclock title %}
{{ super() }} # 用来继承父类已经存在的内容 , 如果不加super(), 那么这个块的内容会被新的覆盖
{% endblock %}
===========================
# 用法
----base.html
{% block title %}
<h1> This is base.html </h1>
{% endblock %}
----page1.html
{% extends "base.html" %}
{% block title %}
{{ super() }}
<h1> This is Page1 html<h1/>
{% endblock %}
======
# 输出:
This is base.html
This is Page1 html
----page2.html
{% extends "base.html" %}
{% block title %}
<h1> This is Page2 html<h1/>
{% endblock %}
======
# 输出:
This is Page2 html
空白控制
默认jinja2 会在命令前后添加空行,所以需要一些控制对他来进行删除
{% if True -%}
{%- if True%}
环境对象删除
app.jinja_env.trim_blocks=True
app.jinja_env.lstrip_blocks = True
注: 宏内的空白不可进行环境对象的配置,只能进行手动删除。
加载静态文件
一个Web项目不仅需要HTML模板,还需要许多静态文件,比如CSS、JavaScript文件、图片以及音频等。在Flask程序中,默认我们需要将静态文件存储在与主脚本(包含程序实例的脚本)同级目录的static文件夹中。
默认路径如下:
url_for('static',filename='')
<img src='{{ url_for("static", filename="avatar.jpg") }}'width='50'/>
引入Bootstrap
{% block styles %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/popper.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
{% endblock %}
CDN链接直接引用
{% block styles %}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
{% endblock %}
...
{% block scripts %}
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
{% endblock %}
使用宏加载静态资源
{% macro static_file(type, filename_or_url, local=True) %}
{% if local %}
{% set filename_or_url = url_for('static', filename=filename_or_url) %}
{% endif %}
{% if type == 'css' %}
<link rel="stylesheet" href="{{ filename_or_url }}" type="text/css">
{% elif type == 'js' %}
<script type="text/javascript" src="{{ filename_or_url }}"></script>
{% elif type == 'icon' %}
<link rel="icon" href="{{ filename_or_url }}">
{% endif %}
{% endmacro %}
----------------------------------------
引用:
static_files('css',css/bootstrap.min.css)
static_file('css', 'https://maxcdn.../css/bootstrap.min.css', local=False)
消息闪现(Flash)
使用功能
flash()函数发送的消息会存储在session中,我们需要在模板中使用全局函数get_flashed_messages()获取消息并将其显示出来。
需要程序密钥
app.secret_key = "secret key"
.env
SECRET_KEY = "secret key"
@app.route("/flash")
def flash1():
flash("欢迎光临")
return render_template('flash.html')
----------------------------
html页面:
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %}
🌞当get_flashed_messages()函数被调用时,session中存储的所有消息都会被移除。如果你这时刷新页面,会发现重载后的页面不再出现这条消息。
自定义错误页面
templates/errors/404.html
{% extends 'base.html' %}
{% block title %}404 - Page Not Found{% endblock %}
{% block content %}
<h1>Page Not Found</h1>
<p>You are lost...</p>
{% endblock %}
错误处理函数需要附加app.errorhandler(code=,name=,description=)装饰器
- code:状态码
- name:原因短语
- description:错误描述,另外使用get_description()还可以获取HTML格式错误代码
from flask import Flask, render_template
@app.errorhandler(404)
def page_not(e):
return render_template('error/404.htmll'), 404
可以注册其他类型的异常
app.errorhandler(NameError)