器→工具, 开源项目, 术→技巧, 研发

Python模板引擎jinja2

钱魏Way · · 384 次浏览
!文章内容如有错误或排版问题,请提交反馈,非常感谢!

Jinja2简介

Jinja2是由 Armin Ronacher 开发的,这位开发者也是 Flask 和 Werkzeug 等著名 Python 项目的作者。Jinja2的设计受到了 Django 模板系统的影响,但在灵活性和性能方面进行了优化。它首次发布于 2008 年,迅速成为 Python 社区中最受欢迎的模板引擎之一。Jinja2的数字”2″,它表示这是 Jinja 模板引擎的第二个主要版本,即 Jinja 的改进版。

主要特性

  • 灵活性和易用性:Jinja2的模板语法直观且易于学习,它允许开发者快速地将数据嵌入到文本文件中。此外,它的语法足够灵活,能够处理复杂的数据结构。
  • 性能:Jinja2的模板渲染非常快速。它通过预编译模板来提高渲染性能,这在大型应用和高负载环境中特别重要。
  • 安全性:自动的 HTML 转义功能帮助预防跨站脚本攻击(XSS)。开发者可以在需要时关闭此功能。
  • 模板继承:Jinja2支持模板继承,这允许创建可重用的模板”基础”,从而减少代码重复。
  • 丰富的内置过滤器和测试:提供了大量的过滤器和测试,使得对数据的处理更加灵活。
  • 宏和自定义函数:可以定义宏(类似于函数),在模板中重复使用。同时,它还允许将自定义的 Python 函数作为过滤器或全局函数使用。
  • 完善的错误处理:Jinja2在模板渲染过程中提供了详细的错误信息,有助于快速定位问题。

适用场景

  • Web开发:Jinja2最常用于网页开发,尤其是与 Flask 和 Django 这样的 Python Web 框架结合使用。它可以生成动态 HTML 页面,是构建动态网站的理想选择。
  • 生成文本文件:除了 HTML,Jinja2也可以用于生成任何文本文件,如 XML、JSON、CSV 等。
  • 邮件模板:在发送电子邮件时,经常需要动态生成邮件内容。Jinja2可以用来创建个性化的邮件模板。
  • 配置文件管理:在需要动态生成配置文件的场景中,如部署脚本、DevOps 工具链,Jinja2也非常有用。
  • 报告生成:可以用于生成结构化的报告文档,尤其是那些需要包含动态数据的报告。

Jinja2的使用示例

Jinja2的使用通常包括两个步骤:创建模板和渲染模板。

  • 创建模板:模板是包含占位符的文本文件,这些占位符将被实际数据所替换。在 Jinja2 中,模板通常使用以下两种占位符:
    • {{ … }}:用于输出变量的值。
    • {% … %}:用于执行逻辑语句,如循环和条件判断。
  • 渲染模板:将数据传递给模板,并生成最终文本。

下面是一个简单的例子,演示了如何使用 Jinja2 渲染一个简单的 HTML 页面:

创建模板

首先,我们创建一个简单的 HTML 模板。这个模板将包含一些基本的 Jinja2 语法,如变量和循环控制结构。

<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<h1>{{ heading }}</h1>
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
</body>
</html>

在这个模板中,{{ title }}、{{ heading }}和 {% for item in items %} 是 Jinja2 的语法元素,用于插入和处理变量。

渲染模板

接下来,使用 Python 脚本来渲染这个模板。我们将传递一个包含所需变量的字典到模板,并输出最终生成的 HTML。

from jinja2 import Environment, FileSystemLoader

# 创建一个加载器,指向模板文件存放的目录
file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader)

# 加载模板
template = env.get_template('your_template.html')

# 定义传递给模板的数据
data = {
'title': 'My Web Page',
'heading': 'Welcome to My Web Page',
'items': ['Item 1', 'Item 2', 'Item 3']
}

# 渲染模板
output = template.render(data)
print(output)

在这个示例中,我们首先加载了存放模板的目录,然后加载了具体的模板文件。通过调用 render 方法,并传递一个包含所需数据的字典,我们得到了最终的 HTML 输出。

请注意,这只是一个基础示例,Jinja2的功能远不止于此。它还支持更复杂的数据结构、条件控制语句、模板继承等高级特性。

Jinja2语法简介

Jinja2控制结构

Jinja2提供了一系列的控制结构,允许在模板中执行更复杂的逻辑。这些控制结构类似于 Python 中的控制结构,但它们是用于模板渲染过程中的。以下是 Jinja2 中一些常用的控制结构:

For循环

  • 用于在模板中遍历序列(如列表或字典)。
  • 语法:{% for item in sequence %}…{% endfor %}。
  • 示例:在 HTML 列表中显示每个元素。
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>

If语句

  • 用于在模板中进行条件判断。
  • 语法:{% if condition %}…{% endif %}。
  • 还可以使用 elif 和 else。
  • 示例:根据条件显示不同的内容。
{% if user.is_admin %}
<p>Welcome, admin!</p>
{% else %}
<p>Welcome, user!</p>
{% endif %}

宏(Macro)

  • 类似于函数,可在模板中定义并重用。
  • 语法:{% macro name(parameters) %}…{% endmacro %}。
  • 可以在其他模板中导入和使用宏。
  • 示例:创建可重用的表单元素。
{% macro input(name, value='', type='text') %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}

Set语句

  • 用于在模板中设置变量。
  • 语法:{% set variable = value %}。
  • 可以设置局部或全局变量。
  • 示例:设置并使用变量。
{% set heading = "Welcome to My Site" %}
<h1>{{ heading }}</h1>

Include 语句

  • 用于包含另一个模板。
  • 语法:{% include ‘filename’ %}。
  • 使得模板更加模块化。
  • 示例:包含共用的头部模板。
{% include 'header.html' %}

继承(Extends)

  • 允许一个模板继承另一个模板的结构。
  • 语法:{% extends ‘base.html’ %}。
  • 结合块(block)使用,允许重写特定部分。
  • 示例:继承基础模板并重写内容块。

这些控制结构提高了模板的灵活性,使得开发者能够创建复杂且动态的网页内容。通过结合这些结构,可以根据不同的数据和条件展示不同的模板内容。

Jinja2 模板继承

Jinja2 的模板继承功能是一个强大的特性,它允许你创建可重用的模板”框架”(通常称为基模板或父模板),然后通过子模板来扩展或重写这个框架的特定部分。这种机制有助于减少重复的代码,使得模板的维护和更新更加方便。以下是关于 Jinja2 模板继承的一些关键概念:

基模板(Base Template)

基模板是一个普通的 Jinja2 模板,它定义了一个通用的页面结构,这个结构可以在多个页面中共享。基模板中通常包含了以下内容:

  • 网站的公共部分,如头部(header)、尾部(footer)。
  • {% block %} 标签,定义了可以被子模板重写的区域。

例如,一个基本的 HTML 基模板可能如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
<title>{% block title %}Default Title{% endblock %}</title>
</head>
<body>
<header>
<!-- 头部内容 -->
</header>

<main>
{% block content %}
<!-- 默认内容 -->
{% endblock %}
</main>

<footer>
<!-- 尾部内容 -->
</footer>
</body>
</html>

子模板(Child Template)

子模板继承自基模板,并可以重写基模板中的一个或多个块(block)。通过继承,子模板继承了基模板的整体结构,同时可以添加或修改特定部分的内容。

要在子模板中继承基模板,使用 {% extends ‘base_template.html’ %} 语句。然后,你可以重写任何定义的块:

{% extends 'base.html' %}
{% block title %}子模板的标题{% endblock %}
{% block content %}
<!-- 这里是子模板的独特内容 -->
{% endblock %}

使用模板继承的优势

  • 重用代码:通过共享基模板的公共结构,减少了重复代码。
  • 易于维护:更新基模板中的共享部分,所有继承它的子模板都会得到更新。
  • 灵活性:子模板可以根据需要重写基模板中的任何部分,实现高度的定制化。

总的来说,Jinja2 的模板继承是一种非常有效的组织和管理模板的方式,尤其是在构建具有共享元素(如导航栏、页脚)的大型网站时。通过继承机制,可以极大地提高模板的可维护性和可扩展性。

Jinja2 过滤器

一个 filter 过滤器的本质就是一个 function 函数。使用格式为:变量名 | 函数。它做到的就是,把变量传给函数,然后再把函数返回值作为这个代码块的值。如:

<!-- 带参数的 -->
{{ 变量 | 函数名(*args) }}

<!-- 不带参数可以省略括号 -->
{{ 变量 | 函数名 }}

链式调用(管道式):和命令行的 pipeline 管道一样,可以一次调用多个函数(过滤器),如:

{{ "hello world" | reverse | upper }}

Jinja2 中内置过滤器

过滤器名 解释 举例
abs(value) 返回一个数值的绝对值 {{ -1 | abs }}
int(value) 将值转换为 int 类型 {{ param | int }}
float(value) 将值转换为 float 类型
string(value) 将变量转换成字符串
default(value, default_value, boolean=false) 如果当前变量没有值,则会使用参数中的值来代替。如果想使用 python 的形式判断是否为 false,则可以传递 boolean=true。也可以使用 or 来替换 {{ name | default(‘xiaotuo’) }}
safe(value) 如果开启了全局转义,那么 safe 过滤器会将变量关掉转义 {{ content_html | safe }}
escape(value) 或 e 转义字符,会将 <、> 等符号转义成 HTML 中的符号 {{ content | escape 或 content | e }}
first(value) 返回一个序列的第一个元素 {{ names | first }}
format(value, *arags, **kwargs) 格式化字符串 %s” – “%s” | format(‘Hello?’, “Foo!”) }} 输出 Hello? – Fool!
last(value) 返回一个序列的最后一个元素。 {{ names | last }}
length(value) 返回一个序列或者字典的长度。 {{ names | length }}
join(value, d=’+’) 将一个序列用 d 这个参数的值拼接成字符串
lower(value) 将字符串转换为小写
upper(value) 将字符串转换为小写
replace(value, old, new) 替换将 old 替换为 new 的字符串
truncate(value, length=255, killwords=False) 截取 length 长度的字符串
striptags(value) 删除字符串中所有的HTML标签,如果出现多个空格,将替换成一个空格
trim 截取字符串前面和后面的空白字符 {{ str123 | trim }}
wordcount(s) 计算一个长字符串中单词的个数

在Jinja2中,除了使用内置过滤器外,你还可以创建自定义过滤器来扩展模板的功能。自定义过滤器允许你在模板中应用特定的数据转换或格式化规则,这在标准过滤器不足以满足需求时非常有用。

创建自定义过滤器

定义一个Python函数:这个函数将成为你的过滤器,它接收至少一个参数(即被过滤的值),并返回一个转换后的值。

示例:

def reverse(s):
    return s[::-1]

将该函数添加到Jinja2环境中:使用Jinja2环境的filters字典,你可以添加自定义的过滤器。

示例:

from jinja2 import Environment

env = Environment()
env.filters['reverse'] = reverse

在模板中使用自定义过滤器:一旦添加了自定义过滤器,你就可以像使用内置过滤器一样在模板中使用它。

示例:

{{ 'Hello World!' | reverse }}

自定义过滤器在很多场景中都非常有用,尤其是在处理特殊的数据格式化或当内置过滤器不满足需求时。例如,你可以创建自定义过滤器来:

  • 格式化日期和时间。
  • 清理或转换用户输入。
  • 应用特定的数学运算。
  • 实现特殊的字符串操作。

注意事项

  • 自定义过滤器应该具有良好的错误处理机制,以避免在模板渲染过程中产生异常。
  • 当创建具有副作用或需要安全注意的过滤器时(如修改全局状态或进行数据库操作),需要特别小心。

通过自定义过滤器,Jinja2的灵活性和功能得到了进一步的扩展,使得模板能够更加精确地满足你的特定需求。

Jinja2模板缓存

Jinja2支持模板缓存,这有助于提高模板渲染的性能,特别是在处理大型或复杂模板时。以下是Jinja2如何实现模板缓存的基本概念:

缓存机制

  • 缓存存储:
    • Jinja2的模板缓存是存储编译后的模板代码,这样在再次渲染同一个模板时,可以直接使用已编译的版本,而无需重新编译。
    • 缓存可以存储在内存中,也可以配置为使用其他类型的后端存储,如文件系统或专门的缓存服务器。
  • 缓存配置:
    • 在创建Jinja2 Environment对象时,可以通过cache_size参数配置缓存的大小。这个值指定了缓存中可以存储多少个模板。
    • 设置为0或None会禁用缓存。
    • 默认情况下,Jinja2使用内存作为缓存。
  • 缓存使用:
    • 当模板被请求渲染时,Jinja2首先检查缓存中是否存在编译后的模板。
    • 如果存在,直接使用缓存的模板进行渲染,从而节省编译时间。
    • 如果不存在,Jinja2会编译模板,然后根据配置将其存储在缓存中。

性能考虑

使用缓存可以显著提高模板渲染的速度,尤其是在频繁渲染同一模板的场景中。

需要注意的是,缓存会占用内存资源。在配置较大的cache_size时,应确保有足够的内存容量。

示例:

from jinja2 import Environment, FileSystemLoader

# 创建一个Environment实例,配置缓存大小
env = Environment(
    loader=FileSystemLoader('/path/to/templates'),
    cache_size=50  # 缓存最多50个模板
)

# 渲染模板时,Jinja2将使用缓存机制
template = env.get_template('my_template.html')
rendered = template.render(some_data)

通过合理配置和使用模板缓存,可以在保持模板灵活性的同时,提高应用程序的响应速度和效率。

Jinja2自动转义

Jinja2的自动转义功能是一项重要的安全特性,主要用于防止跨站脚本攻击(XSS)。在Web开发中,自动转义确保从用户接收的数据在插入HTML时不会执行为恶意代码。以下是关于Jinja2自动转义的一些关键点:

自动转义的工作原理

  • 当启用自动转义时,Jinja2会自动转义所有变量的输出。这意味着如果变量内容包含HTML或JavaScript,它们将被转换成安全的字符实体,从而在浏览器中显示为普通文本,而不是被当作代码执行。
  • 例如,<转换为&lt;,>转换为&gt;,这样浏览器就不会解释这些字符为HTML或JavaScript代码。

启用和禁用自动转义

  • 默认情况下,在渲染HTML模板时,Jinja2会自动启用转义。
  • 可以在创建Jinja2环境时通过autoescape参数来控制自动转义的行为。例如,autoescape=True会启用自动转义,而autoescape=False则会禁用它。
  • 在模板中,可以使用{% autoescape %}标签显式地控制特定区域的转义行为。例如:
{% autoescape true %}
{{ user_input }}
{% endautoescape %}

使用safe过滤器

  • 如果你确信某个变量的内容是安全的,并且不希望它被自动转义,可以使用safe过滤器。例如:
{{ user_input | safe }}
  • 这将告诉Jinja2对user_input变量不进行转义处理,直接原样输出。这个过滤器应该谨慎使用,只有在你完全信任数据内容时才应用它。

安全性考虑

  • 自动转义是一种有效的安全措施,可以防止用户输入被错误地解释为代码,特别是在处理用户生成的内容时。
  • 即使启用了自动转义,也应该在应用程序的其他部分实施良好的输入验证和清理策略。

总之,Jinja2的自动转义功能是保护Web应用免受XSS攻击的关键防线。正确使用并结合其他安全最佳实践,可以有效地提升应用程序的安全性。

Jinja2与Flask

在Flask应用Jinja2模版时,在模版中可以直接调用Flask app中的一些公用变量和方法。

引用Flask的request对象:

<p>{{ request.url }}</p>
<p>{{ request.form.get('name') }}</p>

引用Flask的url_for(…)方法:

<!--它会返回我们定义的路由`app.route('/index')`所对应的URL-->
<p>{{ url_for('index') }}</p>
<!--它会返回我们定义的路由`app.route('/post/{post_id}')`所对应的URL-->
<p>{{ url_for('post', post_id='127') }}</p>
</pre>
在模版中,我们可以引用 get_flashed_messages() 方法,获取 Flask 路由传来的闪现信息:
<pre class="EnlighterJSRAW" data-enlighter-language="generic">% for msg in get_flashed_messages() %}
<p>{{ msg }}</p>
{% endfor %}
</pre>
这种闪现信息是从 Flask 路由中传来的,只要在路由中发一个 flash('hello') 信息,相当于弹了一个 alert()。然后我们可以在 Jinja2 的模版中用 get_flashed_messages() 获得 flash 过来的信息列表。

参考链接:
<ul>
<li><a href="https://palletsprojects.com/p/jinja/">Jinja | The Pallets Projects</a></li>
<li><a href="https://docs.jinkan.org/docs/jinja2/">欢迎来到Jinja2 — Jinja2 2.7 documentation (jinkan.org)</a></li>
</ul>

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注