2

Python模板引擎jinja2

 6 months ago
source link: https://www.biaodianfu.com/jinja2.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Jinja2简介

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

jinja.png

主要特性

  • 灵活性和易用性: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>
<!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)
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>
<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 %}
{% 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 %}
{% 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>
{% set heading = "Welcome to My Site" %}
<h1>{{ heading }}</h1>

Include 语句

  • 用于包含另一个模板。
  • 语法:{% include ‘filename’ %}。
  • 使得模板更加模块化。
  • 示例:包含共用的头部模板。
{% include 'header.html' %}
{% 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>
<!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 %}
{% extends 'base.html' %}
{% block title %}子模板的标题{% endblock %}
{% block content %}
<!-- 这里是子模板的独特内容 -->
{% endblock %}

使用模板继承的优势

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

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

Jinja2过滤器

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

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

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

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

{{ "hello world" | reverse | upper }}
{{ "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]
def reverse(s):
    return s[::-1]

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

from jinja2 import Environment
env = Environment()
env.filters['reverse'] = reverse
from jinja2 import Environment

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

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

{{ 'Hello World!' | 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)
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 %}
{% autoescape true %}
  {{ user_input }}
{% endautoescape %}

使用 safe 过滤器

  • 如果你确信某个变量的内容是安全的,并且不希望它被自动转义,可以使用 safe 过滤器。例如:
{{ user_input|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>
<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>
<!-- 它会返回我们定义的路由`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 %}
% for msg in get_flashed_messages() %}
    <p> {{ msg }} </p>
{% endfor %}

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

参考链接:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK