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

Python模板引擎jinja2

钱魏Way · · 2 次浏览

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)}}

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

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

{{ "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>

在模版中,我们可以引用get_flashed_messages()方法,获取Flask路由传来的闪现信息:

% for msg in get_flashed_messages() %}
    <p> {{ msg }} </p>
{% endfor %}

这种闪现信息是从Flask路由中传来的,只要在路由中发一个flash(‘hello’)信息,相当于弹了一个alert()。然后我们可以在Jinja2的模版中用get_flashed_messages()获得flash过来的信息列表。

参考链接:

发表回复

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