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,它们将被转换成安全的字符实体,从而在浏览器中显示为普通文本,而不是被当作代码执行。
- 例如,<转换为<,>转换为>,这样浏览器就不会解释这些字符为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>