Skip to content

Jinja2:一个快速的 Python 模板引擎

Jinja 是一个快速、富有表现力、可扩展的模板引擎。模板中的特殊占位符允许编写类似于 Python 语法的代码。然后向模板传递数据以渲染最终文档。

有以下特点:

  • 模板可以继承
  • 模板中可以定义和导入宏
  • HTML 模板可以使用自动转义,防止XSS。
  • 沙箱环境可以安全地渲染不受信任的模板。
  • 支持异步生成模板。
  • I18N 国际化。
  • 模板即时编译为优化的 Python 代码并缓存,也可以提前编译
  • 异常指向模板中的正确行,调试更容易。
  • 可扩展的过滤器、测试、函数,甚至语法。

Jinja 代码内容

默认使用如下字符内的才是 Jinja 的 代码内容:

jinja
{% ... %} ## 用于语句
{{ ... }} ## 用于输出到模板的表达式
{# ... #} ## 表示注释

快速开始

安装:

推荐在虚拟环境下安装。

bash
pip install Jinja2

使用模板,如 use_jinja2.py

python
from jinja2 import Template

items = ["one", "two", "three"]

template = """<ul>
{% for item in items %}
    <li>{{ item }}</li>
{% endfor %}
</ul>"""

t = Template(template)
result = t.render(items=items)
print(result)

运行:

bash
python use_jinja2.py

输出:

html
<ul>

    <li>one</li>

    <li>two</li>

    <li>three</li>

</ul>

但是第 2、4、6、8 行是空的,是因为 jinja 的语法也占了行,需要去除 jinja 语法占的行,有两个方式:

1、使用 trim_blocks=True 参数创建模板。

python
# ...
t = Template(template, trim_blocks=True)
# ...

2、在模板语法代码场景里使用 - 减号,如下。

jinja
<ul>
{%- for item in items %}
    <li>{{ item }}</li>
{%- endfor %}
</ul>

过滤器

变量内容有时候需要格式什么的,可以通过过滤器修改。过滤器与变量之间用管道符号 (|) 分隔,如:

jinja
{{ items | join(', ') | title }}

实现功能,items 数组的内容使用 , 接口,再使用 title(首字线大写) 过滤。

输出如下:

text
One, Two, Three

全部内置过滤器请看:官网 List of Builtin Filters

再来一个 join 模板填充的例子,官方有个 tojson() 过滤器。

模板内容:

jinja
{"value": {{ my_text | tojson }}}

模板渲染代码:

python
import json

from jinja2 import Template

my_text = """第一行
第二行,"我是引号"
第三行"""

template = '{"value": {{ my_text | tojson }}}'

t = Template(template, trim_blocks=True)
result = t.render(my_text=my_text)
print(result)

json_ = json.loads(result)
print("成功加载为 json,内容是:", json_)
print(f"前后内容是否一致:{json_['value'] == my_text}")

输出内容:

text
{"value": "\u7b2c\u4e00\u884c\n\u7b2c\u4e8c\u884c\uff0c\"\u6211\u662f\u5f15\u53f7\"\n\u7b2c\u4e09\u884c"}
成功加载为 json,内容是: {'value': '第一行\n第二行,"我是引号"\n第三行'}
前后内容是否一致:True

模板生成的 json 内容是合格,但是 tojson 没法使用更多的参数来控制,比如 ascii 处理。想处理人可读性强一点,怎么办呢?

好在 jinja 提供了自定义过滤器。可以自己实现。

自定义过滤器

想要的功能:

  • 不要把中文转码。
  • 使用过滤器后前后不增加 " 号。这样方便更直观写json模板。

代码如下:

python
import json

from jinja2 import Environment


def to_json(value: str) -> str:
    # 中文不转码
    json_value = json.dumps(value, ensure_ascii=False)
    # 去掉前后的 " 双引号
    return json_value[1:-1]


# 创建一个 jinja 环境
env = Environment()
# 注册自定义过滤器
env.filters['json'] = to_json

my_text = """第一行
第二行,"我是引号"
第三行"""

# 模板中使用自定义过滤器
# 同时单独使用 " 双引号把值括起来,这样更加直观。
template = '{"value": "{{ my_text | json }}"}'
t = env.from_string(template)
result = t.render(my_text=my_text)
print(result)

json_ = json.loads(result)
print("成功加载为 json,内容是:", json_)
print(f"前后内容是否一致:{json_['value'] == my_text}")

主要是:

  • 实现想要的功能定义一个函数
  • 创建一个 jinja 环境
  • 把这个函数注册为自定义过滤器

输出结果:

text
{"value": "第一行\n第二行,\"我是引号\"\n第三行"}
成功加载为 json,内容是: {'value': '第一行\n第二行,"我是引号"\n第三行'}
前后内容是否一致:True

第一行打印出来的 json 可读性更强了。