>>> from env_helper import info; info()
页面更新时间: 2024-04-07 23:44:18
运行环境:
    Linux发行版本: Debian GNU/Linux 12 (bookworm)
    操作系统内核: Linux-6.1.0-18-amd64-x86_64-with-glibc2.36
    Python版本: 3.11.2

6.3. Jinja2模板引擎的用法

Jinja2是基于python的模板引擎,功能比较类似于于PHP的smarty,J2ee的Freemarker和velocity。 它能完全支持unicode,并具有集成的沙箱执行环境,应用广泛。Jinja2使用BSD授权。

Jinja2是Python下一个被广泛应用的模版引擎,他的设计思想来源于Django的模板引擎,并扩展了其语法和一系列强大的功能。其中最显著的一个是增加了沙箱执行功能和可选的自动转义功能。

优点:

  • 沙箱执行模式,模板的每个部分都在引擎的监督之下执行,模板将会被明确地标记在白名单或黑名单内,这样对于那些不信任的模板也可以执行。

  • 强大的自动HTML转义系统,可以有效地阻止跨站脚本攻击。

  • 模板继承机制,此机制可以使得所有的模板都具有相似一致的布局,也方便了开发人员对模板的修改和管理。

  • 高效的执行效率,Jinja2引擎在模板第一次加载时就把源码转换成Python字节码,加快模板执行时间。

  • 可选的预编译模式。

  • 调试系统融合了标准的Python的TrackBack系统,使得模板编译和运行期间的错误能及时被发现和调试。

  • 语法可配置,可以重新配置Jinja2使得它更好地适应LaTeX或JavaScript的输出。

6.3.1. 安装

pip install jinja2

导入一个模块测试,我们写一个简单的例子。

>>> from jinja2 import Template
>>> template = Template('Hello {{ name }}!')
>>> template.render(name='John Doe')
'Hello John Doe!'

通过创建一个 Template 的实例,你会得到一个新的模板对象,提供一 个名为 render() 的方法,该方法在有字典或关键字参数时调用 扩充模板。字典或关键字参数会被传递到模板,即模板“上下文”。Jinja2 内部使用 unicode 并且返回值也是 unicode 字符串。所以确 保你的应用里也确实使用 unicode

6.3.2. 基本结构

模板仅仅是文本文件。它可以生成任何基于文本的格式(HTML、XML、CSV、LaTex 等等)。 它并没有特定的扩展名, .html 或 .xml 都是可以的。

模板包含 变量 或 表达式 ,这两者在模板求值的时候会被替换为值。模板中 还有标签,控制模板的逻辑。模板语法的大量灵感来自于 Django 和 Python 。

下面是一个最小的模板,它阐明了一些基础。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head>

<title>My Webpage</title>

</head> <body>

<ul id="navigation"> {% for item in navigation %}

<li><a href="{{ item.href }}">{{ item.caption }}</a></li>

{% endfor %} </ul>

<h1>My Webpage</h1> {{ a_variable }}

</body> </html>

这包含了默认的设定。应用开发者会把语法从 {% foo %} 改成 <% foo %> 或类似的东西。

这里有两种分隔符: {% ... %}{{ ... }}{% ... %} 用于执行语句,{{ ... }} 输出语句结果到模板。

了解了这些Jinja2的使用就基本掌握了,需要的是一些细节信息。

6.3.3. 变量

应用把变量传递到模板,你可能在模板中弄混。变量上面也可以有你能访问的属性或元素。变量看起来是什么,完全取决于应用提供了什么。

你可以使用点( . )来访问变量的属性,作为替代,也可以使用所谓的“下标”语 法( [] )。下面的几行效果是一样的:

{{ foo.bar }}
{{ foo['bar'] }}

花括号不是变量的一部分,而是打印语句的一部分。 如果变量或属性不存在,会返回一个未定义值。你可以对这类值做什么取决于应用的配 置,默认的行为是它如果被打印,其求值为一个空字符串,并且你可以迭代它,但其它操作会失败。

过滤器

变量可以通过 过滤器 修改。过滤器与变量用管道符号( | )分割,并且也 可以用圆括号传递可选参数。多个过滤器可以链式调用,前一个过滤器的输出会被作为 后一个过滤器的输入。

  • safe 渲染时值不转义

  • capitialize 把值的首字母转换成大写,其他子母转换为小写

  • lower 把值转换成小写形式

  • upper 把值转换成大写形式

  • title 把值中每个单词的首字母都转换成大写

  • trim 把值的首尾空格去掉

  • striptags 渲染之前把值中所有的HTML标签都删掉

  • join 拼接多个值为字符串

  • replace 替换字符串的值

  • round 默认对数字进行四舍五入,也可以用参数进行控制

  • int 把值转换成整型

使用方法

>>> template = Template('Hello {{ "abc" | upper   }}!')
>>> template.render()
'Hello ABC!'
>>> template = Template('Hello {{ "abc" | title  }}!')
>>> template.render()
'Hello Abc!'

方法还有很多这只是比较常见的几个。查看地址 http://docs.jinkan.org/docs/jinja2/templates.html#builtin-filters

6.3.4. 注释及转义

注释

要把模板中一行的部分注释掉,默认使用 {# … #} 注释语法。

{#
    {% for user in users %}
        ...
    {% endfor %}
#}
>>> template = Template('Hello {# {% for user in users %}{{ user }}{% endfor %}#} {{ name  }}!')
>>> template.render(users=["Jerry","Bob","Jack"],name="Tom")
'Hello  Tom!'
>>> template = Template('Hello {% for user in users %} {{ user }} {% endfor %} {{ name  }}!')
>>> template.render(users=["Jerry","Bob","Jack"],name="Tom")
'Hello  Jerry  Bob  Jack  Tom!'

默认配置中,模板引擎不会对空白做进一步修改,所以每个空白(空格、制表符、换行符 等等)都会原封不动返回。如果应用配置了 Jinjatrim_blocks ,模板标签后的 第一个换行符会被自动移除。

此外,你也可以手动剥离模板中的空白。当你在块(比如一个 for 标签、一段注释或变 量表达式)的开始或结束放置一个减号( - ),可以移除块前或块后的空白:

>>> template = Template('Hello {% for user in users -%} {{ user }} {%- endfor %} {{ name  }}!')
>>> template.render(users=["Jerry","Bob","Jack"],name="Tom")
'Hello JerryBobJack Tom!'

转义

有时想要或甚至必要让 Jinja 忽略部分,不会把它作为变量或块来处理。例如,如果 使用默认语法,你想在在使用把 {{ 作为原始字符串使用,并且不会开始一个变量 的语法结构,你需要使用一个技巧。

最简单的方法是在变量分隔符中( {{ )使用变量表达式输出:

{{ '{{' }}
>>> template = Template('Hello {% for user in users -%} {{ "{{" }} {%- endfor %} {{ name  }}!')
>>> template.render(users=["Jerry","Bob","Jack"],name="Tom")
'Hello {{{{{{ Tom!'

6.3.5. 模板继承

Jinja 中最强大的部分就是模板继承。模板继承允许你构建一个包含你站点共同元素的基 本模板“骨架”,并定义子模板可以覆盖的 块 。

听起来复杂,实际上很简单。从例子上手是最易于理解的。

基本模板

这个模板,我们会把它叫做 base.html ,定义了一个简单的 HTML 骨架文档,你可 能使用一个简单的两栏页面。用内容填充空的块是子模板的工作:

<html lang="en">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    {% block head %}
    <link rel="stylesheet" href="style.css" />
    <title>{% block title %}{% endblock %} - My Webpage</title>
    {% endblock %}
</head>
<body>
    <div id="content">{% block content %}{% endblock %}</div>
    <div id="footer">
        {% block footer %}
        &copy; Copyright 2008 by <a href="http://domain.invalid/">you</a>.
        {% endblock %}
    </div>
</body>

{% block %} 标签定义了四个子模板可以填充的块。所有的 block 标签 告诉模板引擎子模板可以覆盖模板中的这些部分。

子模板

子模板是为了将基模板的block内容进行重写。

{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
    {{ super() }}
    <style type="text/css">
        .important { color: #336699; }
    </style>
{% endblock %}
{% block content %}
    <h1>Index</h1>
    <p class="important">
      Welcome on my awesome homepage.
    </p>
{% endblock %}

{% extend %} 标签是这里的关键。它告诉模板引擎这个模板“继承”另一个模板。 当模板系统对这个模板求值时,首先定位父模板。这有点像java里子类继承父类,在将父类的方法进行重写。

模板的文件名依赖于模板加载器。例如 FileSystemLoader 允许你用文件名访问其它模板。你可以使用斜线访问子目录中的模板:

{% extends "layout/default.html" %}

6.3.6. 控制结构清单

控制结构指的是所有的那些可以控制程序流的东西 —— 条件(比如 if/elif/else )、for 循环、以及宏和块之类的东西。控制结构在默认语法中以 {% .. %} 块的形式出现。

语法与python基本相同,只要注意几个细节就可以完全掌握。

For循环

遍历序列中的每项。例如,要显示一个由 users 变量提供的用户列表,值得注意的是loop属性,在一个 for 循环块中你可以访问这些特殊的变量:

  • loop.index 当前循环迭代的次数(从 1 开始)

  • loop.index0 当前循环迭代的次数(从 0 开始)

  • loop.revindex 到循环结束需要迭代的次数(从 1 开始)

  • loop.revindex0 到循环结束需要迭代的次数(从 0 开始)

  • loop.first 如果是第一次迭代,为 True 。

  • loop.last 如果是最后一次迭代,为 True 。

  • loop.length 序列中的项目数。

  • loop.cycle 在一串序列间期取值的辅助函数。

这个循环 10 次迭代之后会终止处理:

>>> template = Template('{% for user in users %}{% if loop.index >= 10 %}{% else %} {{ user }} {% endif %}  {% endfor %}')
>>> template.render(users=[1,2,3,4,5,6,7,8,9,10,11])
' 1    2    3    4    5    6    7    8    9       '

If语句

Jinja 中的 if 语句可比 Python 中的 if 语句。在最简单的形式中,你可以测试 一个变量是否未定义,为空或 false:

>>> template = Template('Hello {% for user in users %}{% if user %} {{ user }}  {% else %} empty {% endif %}{% endfor %}')
>>> template.render(users=["Jerry","Bob","","Jack"])
'Hello  Jerry   Bob   empty  Jack  '

If语句可用的条件操作符

  • == 比较两个对象是否相等。

  • != 比较两个对象的不等式。

  • > 如果左侧大于右侧,则为true。

  • >= 如果左侧大于或等于右侧,则为true。

  • < 如果左侧小于右侧,则为true。

  • <= 如果左侧小于或等于右侧,则为true。

If语句逻辑连接符

  • and 如果左右操作数为真,则返回true。

  • or 如果左操作数或右操作数为真,则返回true。

  • not 非,not x :如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。

宏类似常规编程语言中的函数。它们用于把常用行为作为可重用的函数,取代手动重复的工作。

这里是一个宏渲染表单元素的小例子:

{% macro input(name, value='', type='text', size=20) -%}
    <input type="{{ type }}" name="{{ name }}" value="{{value|e }}" size="{{ size }}">
{%- endmacro %}

在命名空间中,宏之后可以像函数一样调用:

<p>{{ input('username') }}</p>
<p>{{ input('password', type='password') }}</p>

如果宏在不同的模板中定义,你需要首先使用 import

定义宏(forms.html):

{% macro input(name, value='', type='text') -%}
    <input type="{{ type }}" value="{{ value|e }}" name="{{ name }}">
{%- endmacro %}

{%- macro textarea(name, value='', rows=10, cols=40) -%}
    <textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols
        }}">{{ value|e }}</textarea>
{%- endmacro %}

使用宏(forms.html)

{% import 'forms.html' as forms %}
<dl>
    <dt>Username</dt>
    <dd>{{ forms.input('username') }}</dd>
    <dt>Password</dt>
    <dd>{{ forms.input('password', type='password') }}</dd>
</dl>
<p>{{ forms.textarea('comment') }}</p>

或者是

{% from 'forms.html' import input as input_field, textarea %}

宏与宏之间的交互

{% macro render_dialog(title, class='dialog') -%}
    <div class="{{ class }}">
        <h2>{{ title }}</h2>
        <div class="contents">
            {{ caller() }}
        </div>
    </div>
{%- endmacro %}

{% call render_dialog('Hello World') %}
    This is a simple dialog rendered by using a macro and
    a call block.
{% endcall %}

6.3.7. 利用jinja2进行渲染

Environment:这个类的实例用于存储配置和全局对象,然后从文件系统或其他位置中加载模板。

FileSystemLoader:文件系统加载器,不需要模板文件存在某个Python包下,可以直接访问系统中的文件。

创建一个Environment加载器对象:

>>> from jinja2 import FileSystemLoader,Environment
>>> env = Environment(loader=FileSystemLoader('templates'))

get_template():获取模板目录下的某个具体文件

加载一个模板文件:

>>> template = env.get_template('base.html')

render():接受变量,对模板进行渲染

渲染,将模板内的变量进行处理。

>>> template.render(name="test_name",age=18)
'<html lang="en">n  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>n<head>n    <title>n My Webpage  n</title>n</head>n<body>n    n<h1></h1>n    test_namen18n</body>n</html>'