局部模板
在Web程序中,我们通常会为每一类页面编写一个独立的模板。比如主页模板、用户资料页模板、设置页模板等。
这些模板可以直接在视图函数中渲染并作为HTML响应主体。除了这类模板,我们还会用到另一类非独立模板,这类模板通常被称为局部模板或次模板,因为它们仅包含部分代码,所以我们不会在视图函数中直接渲染它,而是插入到其他独立模板中。
当多个独立模板中都会使用同一块HTML代码时,我们可以把这部分代码抽离出来,存储到局部模板中。这样一方面可以避免重复,另一方面也可以方便统一管理。
比如,多个页面中都要在页面顶部显示一个提示条,这个横幅可以定义在局部模板_banner.html中。
我们使用include标签来插入一个局部模板,这会把局部模板的全部内容插在使用include标签的位置。比如,在其他模板中,我们可以在任意位置使用下面的代码插入_banner.html的内容:
{% include '_banner.html' %}
宏
宏(macro)是Jinja2提供的一个非常有用的特性,它类似Python中的函数。使用宏可以把一部分模板代码封装到宏里,使用传递的参数来构建内容,最后返回构建后的内容。
为了便于管理,我们可以把宏存储在单独的文件中,这个文件通常命名为macros.html或_macors.html。
在创建宏时,我们使用macro和 endmacro标签声明宏的开始和结束。
{% macro qux(amount=1) %}
{% if amount == 1 %}
I am qux.
{% elif amount > 1 %}
We are quxs.
{% endif %}
{% endmacro %}
使用时,需要像从Python模块中导入函数一样使用import语句导入它,然后作为函数调用,传入必要的参数
{% from 'macros.html' import qux %}
...
{{ qux(amount=5) }}
模板继承
Jinja2的模板继承允许你定义一个基模板,把网页上的导航栏、页脚等通用内容放在基模板中,而每一个继承基模板的子模板在被渲染时都会自动包含这些部分。使用这种方式可以避免在多个模板中编写重复的代码。
子模板追加
如果想要向基模板中的块追加内容,需要使用Jinja2提供的 super()函数进行声明,这会向父块添加内容。
比如,下面的示例向基模板中的styles块追加了一行
{% block styles %}
{{ super() }}
<style>
.foo {
color: red;
}
</style>
{% endblock %}
实践:编写基模板
先写一个模板:base.html
{# 这个基模板中,创建了六个块,
head、title、styles、 content、footer和scripts,分别用来划分不同的代码
其中,head块表示 <head>标签的内容,
title表示<title>标签的内容,
content块表示页面主体内容,
footer表示页脚部分,
styles块和scripts块,则分别用来包含CSS文件和JavaScript文件引用链接或页内的CSS和JavaScript代码#}
<!DOCTYPE html>
<html>
<head>
{# 模板中由”{% block block_name %}”和”{% endblock %}”所包括的语句块,
将会替换父模板中同样由”{% block block_name %}”和”{% endblock %}”所包括的语句块。 #}
{% block head %}
<meta charset="utf-8">
<title>{% block title %}Template - HelloFlask{% endblock %}</title>
{% block styles %}{% endblock %}
{% endblock %}
</head>
<body>
<nav>
{# <ul> 标签定义无序列表。 #}
<ul><li><a href="{{ url_for('index') }}">Home</a></li></ul>
</nav>
<main>
{% block content %}{% endblock %}
</main>
<footer>
{% block footer %}
...
{% endblock %}
</footer>
{% block scripts %}{% endblock %}
</body>
</html>
以content块为例,继承的模块如图
编写一个子模板:index.html
{# 指的是继承了base.html #}
{% extends 'base.html' %}
{# 调用宏 导入 #}
{% from 'macros.html' import qux %}
{# 对flask里的content块进行渲染 #}
{% block content %}
{% set name='baz' %}
<h1>Template</h1>
<ul>
<li><a href="{{ url_for('watchlist') }}">Watchlist</a></li>
<li>Filter: {{ foo|musical }}</li>
<li>Global: {{ bar() }}</li>
<li>Test: {% if name is baz %}I am baz.{% endif %}</li>
<li>Macro: {{ qux(amount=5) }}</li>
</ul>
{% endblock %}
书上遗漏的点
<li>Filter: {{ foo|musical }}</li>
musical未找到
musical是自定义的过滤器,过滤器函数接收s作为被过滤的变量值,返回处理后的值。
创建的musical过滤器会在被过滤的变量字符后面添加一个音符(single bar note)图标,音符通过HTML 实体♫;表示,我们使用Markup类将它标记为安全字符(将文本标记为安全的一种方法是在渲染前将变量转换为Markup对象)
@app.template_filter()
def musical(s):
return s+Markup(' ♫ ')
错误:未在app.jinja_env.tests里添加自定义测试器
测试器:和过滤器十分相似,区别在于测试器总是返回一个布尔值,它可以用来测试一个变量或者表达式
解决方法:添加代码
def baz(n):
if n == 'baz':
return True
return False
app.jinja_env.tests['baz'] = baz
- 未编写宏html
宏(macro)是Jinja2提供的一个非常有用的特性,它类似Python中的函数。使用宏可以把一部分模板代码封装到宏里,使用传递的参数来构建内容,最后返回构建后的内容。
在功能上,它和局部模板类似,都是为了方便代码块的重用。
解决办法:新建一个macros.html 附上代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% macro qux(amount=1) %}
{% if amount == 1 %}
I am qux.
{% elif amount > 1 %}
We are quxs.
{% endif %}
{% endmacro %}
</body>
</html>
调用的时候输入<li>Macro: {{ qux(amount=5) }}</li>
即可
- 未注册watchlist.html
重新写一个watchlist.html 再在app.py里加上
user = {
'username': 'Grey Li',
'bio': 'A boy who 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.route('/watchlist')
def watchlist():
return render_template('watchlist.html',user=user,movies=movies)
- 未注册全局函数和全局对象
def bar():
return 'I am bar.'
foo = 'I am foo.'
app.jinja_env.globals['bar'] = bar
app.jinja_env.globals['foo'] = foo
app.py代码
from flask import Flask,render_template
from flask import Markup
app = Flask(__name__)
user = {
'username': 'Grey Li',
'bio': 'A boy who 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'},
]
def baz(n):
if n == 'baz':
return True
return False
app.jinja_env.tests['baz'] = baz
def bar():
return 'I am bar.'
foo = 'I am foo.'
app.jinja_env.globals['bar'] = bar
app.jinja_env.globals['foo'] = foo
@app.template_filter()
def musical(s):
return s+Markup(' ♫ ')
@app.route('/')
def index():
return render_template('index.html')
@app.route('/watchlist')
def watchlist():
return render_template('watchlist.html',user=user,movies=movies)
if __name__ == '__main__':
app.run()
渲染子模板
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Template - HelloFlask</title>
</head>
<body>
<nav>
<ul><li><a href="/">Home</a></li></ul> 、
</nav>
<main>
<h1>Template</h1>
<ul>
<li><a href="/watchlist">Watchlist</a></li>
<li>Filter: I am foo. ♫</li>
<li>Global: I am bar.</li>
<li>Test: I am baz.</li>
<li>Macro: We are quxs.</li>
</ul>
</main>
<footer>
...
</footer>
</body>
</html>