3.1 Jinja2 模板引擎

Jinja2 是一种面向Python的现代且易于设计的模板语言。在该部分介绍 Jinja2 的语法和语义结构。

3.1.1 变量

模板变量通过上下文字典定义并传递给模板。在模板中使用 {{ variable }} 结构表示一个变量,这是一种特殊的占位符,告诉模板引擎这个位置的值从渲染模板时使用的数据获取。

Jinja2 能识别所有类型的变量,如列表、字典和对象等。如果要访问变量中的属性,可以使用.[]来访问。

{{ mydict['key'] }}
{{ mydict.key }}
{{ myobj.attr }}
{{mylist[2]}}

变量的值可以使用过滤器修改。过滤器添加在变量名之后,二者之间以竖线分隔,例如将变量的值变为首字母大写的形式:

{{ name|capitalize}}

Jinja2 提供的常用过滤器:

过滤器名

说明

safe

渲染时不转义

capitalize

把值的首字母转换成大写,其他字母转换成小写

lower

把值转换成小写形式

upper

把值转换成大写形式

title

把值中每个单词的首字母转换成大写

trim

把值的首尾空格删掉

striptags

渲染之前把值中所有的 HTML 标签删掉

3.1.2 注释

如果要把模板中的一部分内容注释掉,使用 {# ... #}

{# note: disabled template because we no longer use this
    {% for user in users %}
        ...
    {% endfor %}
#}

3.1.3 控制结构

Jinja2 提供了多种控制结构,可用来改变模板的渲染流程。

  • 条件控制(if-elif-else)

{% if condition1 %}
    ...
{% elif condition2 %}
    ...
{% else %}
    ...
{% endif %}
  • for 循环

<ul>
{% for comment in comments%}
    <li>{{ comment }}</li>
{% endfor %}
</ul>

可以结合 if 进行一些条件过滤:

{% for comment in comments if condition %}
    ...
{% endfor %}

宏类似 Python 中的函数。

{% macro render_comment(comment) %}
    <li>{{ comment }}</li>
{% endmacro %}

调用宏:

<ul>
    {% comment in comments %}
        {{ render_comment(comment) }}
    {% endfor %}
</ul>

为了重复使用宏,可以把宏保存在单独的文件中,然后再需要使用的模板中导入:

{% import 'macros.html' as macros %}
<ul>
    {% comment in comments %}
        {{ macros.render_comment(comment) }}
    {% endfor %}
</ul>

3.1.4 模板继承

模板继承是指将公用的一部分代码抽取出来放到一个基模板中,然后子模板继承这部分内容。模板继承包括基模板和子模板。基模板里包含了网站里基本元素的基本骨架,但里面有一些空的或不完善的块(block)需要用子模板来填充。

继承语法:

{% extends “模板名称” %}

Jinja2 使用 blockendblock 指定在基模板中定义内容区块。

示例:在templates目录中创建base.html、index.html文件。

base.html 模板的内容:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
    {% block head %}
    <title>{% block title %}{% endblock %} - 我的网站</title>
    {% endblock %}
</head>
<body>
    {% block body %}
        这是基模板(base.html)中的内容
    {% endblock %}
</body>
</html>

index.html 模板的内容:

{% extends "base.html" %}
{% block title %}网站首页{% endblock %}
{% block body %}
    {{ super() }}
    <h4>这是网站首页(index.html)的内容!</h4>
{% endblock %}

应用主程序app.py:

from flask import Flask,render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

if __name__ == "__main__":
    app.run(debug=True)

执行主程序 app.py 后打开 http://127.0.0.1:5000/ 可以看到网站内容:

默认情况下,如果子模块实现了父模板定义的 block,那么子模板 block 中的代码就会覆盖父模板中的代码。例如,上述示例中在子模板 index.html 中 {% block title %} 定义内容会覆盖基模板 base.html 中的相应位置。如果想在子模版中仍然保持父模版代码,需要用 super() 函数调用。此外,如果想要在一个 block 中调用其他 block中的代码,可以通过 {{ self.其他block名称() }} 实现。