# Python Jinja 教程

> 原文： [http://zetcode.com/python/jinja/](http://zetcode.com/python/jinja/)

Jinja 教程展示了如何使用 Jinja 模块在 Python 中创建模板。

## Python Jinja 模块

Jinja 是 Python 的模板引擎。 它类似于 Django 模板引擎。

模板引擎或模板处理器是一个旨在将模板与数据模型结合以生成文档的库。 模板引擎通常用于在源代码预处理中生成大量电子邮件，或生成动态 HTML 页面。

我们创建一个模板引擎，在其中定义静态零件和动态零件。 动态部分随后将替换为数据。 渲染功能随后将模板与数据结合在一起。

## Jinja 安装

```py
$ sudo pip3 install jinja2

```

我们使用`pip3`工具安装 Jinja。

## Jinja 分隔符

Jinja 在模板字符串中使用各种分隔符。

*   `{% %}`-陈述
*   `{{ }}`-要打印到模板输出的表达式
*   `{# #}`-模板输出中未包含的注释
*   `# ##`-行语句

## Jinja 简单的例子

在第一个示例中，我们创建一个非常简单的模板。

`simple.py`

```py
#!/usr/bin/env python3

from jinja2 import Template

name = input("Enter your name: ")

tm = Template("Hello {{ name }}")
msg = tm.render(name=name)

print(msg)

```

该示例要求输入用户名并生成消息字符串，该消息字符串将打印给用户。 模板引擎类似于 Python `format()`方法； 但是模板引擎功能更强大，并且具有更多功能。

```py
from jinja2 import Template

```

我们从`jinja2`模块导入`Template`对象。 `Template`是中央模板对象。 它代表一个已编译的模板，并用于对其进行求值。

```py
tm = Template("Hello {{ name }}")

```

在我们的模板中，我们具有用于打印变量的`{{ }}`语法。 变量以`render()`方法传递。

```py
msg = tm.render(name=name)

```

使用`render()`方法，我们生成最终输出。 该方法将模板字符串与作为参数传递的数据连接在一起。 传递给`render()`方法的变量称为上下文变量。

```py
$ ./simple.py
Enter your name: Paul
Hello Paul

```

这是一个示例输出。

在下一个示例中，我们使用两个变量。

`simple2.py`

```py
#!/usr/bin/env python3

from jinja2 import Template

name = 'Peter'
age = 34

tm = Template("My name is {{ name }} and I am {{ age }}")
msg = tm.render(name=name, age=age)

print(msg)

```

模板字符串呈现两个变量：名称和年龄。 这次变量是硬编码的。

```py
$ ./simple2.py 
My name is Peter and I am 34

```

这是输出。

## Jinja 对象

我们可以使用模板字符串中的对象。

`objects.py`

```py
#!/usr/bin/env python3

from jinja2 import Template

class Person:

    def __init__(self, name, age):

        self.name = name
        self.age = age

    def getAge(self):
        return self.age

    def getName(self):
        return self.name    

person = Person('Peter', 34)

tm = Template("My name is {{ per.getName() }} and I am {{ per.getAge() }}")
msg = tm.render(per=person)

print(msg)

```

在示例中，我们定义了`Person`对象。 我们通过两个获取器获取名称和年龄。

## 字典

Jinja 允许使用方便的点表示法来访问 Python 字典中的数据。

`dicts.py`

```py
#!/usr/bin/env python3

from jinja2 import Template

person = { 'name': 'Person', 'age': 34 }

tm = Template("My name is {{ per.name }} and I am {{ per.age }}")
# tm = Template("My name is {{ per['name'] }} and I am {{ per['age'] }}")
msg = tm.render(per=person)

print(msg)

```

我们有一本人字典。 我们使用点运算符访问字典键。

```py
tm = Template("My name is {{ per.name }} and I am {{ per.age }}")
# tm = Template("My name is {{ per['name'] }} and I am {{ per['age'] }}")

```

活动方式和注释方式均有效。 点符号更方便。

## Jinja 原始数据

我们可以使用`raw`和`endraw`标记转义 Jinja 分隔符。

`raw_data.py`

```py
#!/usr/bin/env python3

from jinja2 import Template

data = '''
{% raw %}
His name is {{ name }}
{% endraw %}
'''

tm = Template(data)
msg = tm.render(name='Peter')

print(msg)

```

通过使用`raw`和`endraw`块，我们逃避了 Jinja `{{ }}`语法。 它是按字面意思印刷的。

## Jinja 转义数据

为了转义诸如`<`或`>`字符之类的数据，我们可以使用过滤器或`escape()`函数。

`escape_data.py`

```py
#!/usr/bin/env python3

from jinja2 import Template, escape

data = '<a>Today is a sunny day</a>'

tm = Template("{{ data | e}}")
msg = tm.render(data=data)

print(msg)
print(escape(data))

```

该示例转义`<`和`>`字符。

```py
tm = Template("{{ data | e}}")

```

使用`e`过滤器，可以转储数据。 过滤器应用`|`字符。

```py
print(escape(data))

```

转义功能执行相同的操作。

## Jinja 表达式

for 表达式用于迭代模板中的数据收集。

现在，我们不再使用简单的字符串模板。 我们使用一个`FileSystemLoader`加载的文本文件。

`for_expr.py`

```py
#!/usr/bin/env python3

from jinja2 import Environment, FileSystemLoader

persons = [
    {'name': 'Andrej', 'age': 34}, 
    {'name': 'Mark', 'age': 17}, 
    {'name': 'Thomas', 'age': 44}, 
    {'name': 'Lucy', 'age': 14}, 
    {'name': 'Robert', 'age': 23}, 
    {'name': 'Dragomir', 'age': 54}
]

file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader)

template = env.get_template('showpersons.txt')

output = template.render(persons=persons)
print(output)

```

在此示例中，模板是`showpersons.txt`文件。 该文件位于`templates`目录中。

```py
persons = [
    {'name': 'Andrej', 'age': 34}, 
    {'name': 'Mark', 'age': 17}, 
    {'name': 'Thomas', 'age': 44}, 
    {'name': 'Lucy', 'age': 14}, 
    {'name': 'Robert', 'age': 23}, 
    {'name': 'Dragomir', 'age': 54}
]

```

数据是字典列表。

```py
file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader)

```

我们定义一个`FileSystemLoader`。 从`templates`目录中检索模板。

```py
template = env.get_template('showpersons.txt')

```

我们使用`get_template()`方法获得模板。

`templates/showpersons.txt`

```py
{% for person in persons -%}
    {{ person.name }} {{ person.age }}
{% endfor %}

```

在模板文件中，我们使用`for`表达式遍历集合。 我们显示此人的姓名和年龄。 `%`字符旁边的破折号用于控制空格。

## Jinja 条件

条件是满足特定条件时要求值的表达式。

`conditionals.py`

```py
#!/usr/bin/env python3

from jinja2 import Environment, FileSystemLoader

persons = [
    {'name': 'Andrej', 'age': 34}, 
    {'name': 'Mark', 'age': 17}, 
    {'name': 'Thomas', 'age': 44}, 
    {'name': 'Lucy', 'age': 14}, 
    {'name': 'Robert', 'age': 23}, 
    {'name': 'Dragomir', 'age': 54}, 
]

file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader)
env.trim_blocks = True
env.lstrip_blocks = True
env.rstrip_blocks = True

template = env.get_template('showminors.txt')

output = template.render(persons=persons)
print(output)

```

该示例仅打印未成年人。 未成年人是指未满 18 岁的人。

```py
env.trim_blocks = True
env.lstrip_blocks = True
env.rstrip_blocks = True

```

输出中的空白可以通过环境属性来控制。

`templates/showminors.txt`

```py
{% for person in persons %}
    {% if person.age < 18 %}
        {{- person.name }}
    {% endif %}    
{%- endfor %}

```

在模板中，我们仅使用`if`表达式输出 18 岁以下的人。

```py
$ ./conditionals.py
Mark
Lucy

```

这是输出。

## Jinja `sum`过滤器

可以将过滤器应用于数据以对其进行修改。 例如，`sum`过滤器可以对数据求和，`escape`过滤器对它们进行转义，`sort`过滤器对它们进行排序。

`sum_filter.py`

```py
#!/usr/bin/env python3

from jinja2 import Environment, FileSystemLoader

cars = [
    {'name': 'Audi', 'price': 23000}, 
    {'name': 'Skoda', 'price': 17300}, 
    {'name': 'Volvo', 'price': 44300}, 
    {'name': 'Volkswagen', 'price': 21300}
]

file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader)

template = env.get_template('sumprices.txt')

output = template.render(cars=cars)
print(output)

```

在示例中，我们使用`sum`过滤器来计算所有汽车价格的总和。

```py
cars = [
    {'name': 'Audi', 'price': 23000}, 
    {'name': 'Skoda', 'price': 17300}, 
    {'name': 'Volvo', 'price': 44300}, 
    {'name': 'Volkswagen', 'price': 21300}
]

```

我们有汽车字典的列表。 每个字典都有一个价格键。 它将用于计算总和。

`templates/sumprices.txt`

```py
The sum of car prices is {{ cars | sum(attribute='price') }}

```

在模板文件中，我们将过滤器应用于汽车收集对象。 根据`price`属性计算总和。

```py
$ ./sum_filter.py 
The sum of car prices is 105900

```

这是输出。

## Jinja 模板继承

模板继承是一项强大的功能，可减少代码重复并改善代码组织。 我们定义了一个基本模板，其他模板文件也从中继承。 这些模板文件将覆盖基本模板文件的特定块。

`ineritance.py`

```py
#!/usr/bin/env python3

from jinja2 import Environment, FileSystemLoader

content = 'This is about page'

file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader)

template = env.get_template('about.html')

output = template.render(content=content)
print(output)

```

我们渲染`about.html`文件。 它继承自`base.html`文件。

`base.html`

```py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    {% block content%}

    {% endblock %}
</body>
</html>

```

在`base.html`文件中，我们声明两个块：标题和内容。 这些块将在子模板中填充特定的标签和文本。

`about.html`

```py
{% extends 'base.html' %}

{% block title%}About page{% endblock %}

{% block content %}
<h1>About page</h1>
<p>
    This is about page
</p>
{% endblock %}

```

`about.html`模板文件继承自`base.html`。 它添加了特定于此页面的数据。 我们避免代码重复； 我们不会在两个页面上重复相同的标签，例如`body`和`html`和`meta`标签。

```py
{% extends 'base.html' %}

```

继承是通过`extends`指令完成的。

```py
{% block title%}About page{% endblock %}

```

我们定义一个标题。

```py
{% block content %}
<h1>About page</h1>
<p>
    This is about page
</p>
{% endblock %}

```

并且我们定义内容。

## Jinja Flask 示例

在下一个示例中，我们创建一个使用 Jinja 的简单 Flask 应用。

`app.py`

```py
#!/usr/bin/env python3

from flask import Flask, render_template, request
app = Flask(__name__)

@app.route("/greet")
def greet():
    username = request.args.get('name')
    return render_template('index.html', name=username)

if __name__ == "__main__":
    app.run()

```

在此 Flask 应用中，我们获取用户名，并将其作为参数传递给`render_template()`方法。 `greet()`功能对`/greet`路径做出反应。

`templates/index.html`

```py
<!doctype html>

<html lang="en">

<head>
    <meta charset="utf-8">
    <title>Greeting</title>
</head>

<body>
    <p>
        Hello {{ name }}
    </p>
</body>

</html>

```

这是模板文件，位于`templates`目录中。 我们使用`{{ name }}`语法将用户名添加到模板文件中。

```py
$ python3 app.py 
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

```

我们启动服务器。

```py
$ curl http://127.0.0.1:5000/greet?name=Peter
<!doctype html>

<html lang="en">

<head>
    <meta charset="utf-8">
    <title>Greeting</title>
</head>

<body>
    <p>
        Hello Peter
    </p>
</body>

</html>

```

我们使用`curl`工具连接到应用。 我们添加一个名称参数。

在本教程中，我们介绍了 Python Jinja 模块。

您可能也对以下相关教程感兴趣： [PyMongo 教程](/python/pymongo/)， [Python 日志记录教程](/python/logging/)， [pyDAL 教程](/python/pydal/)和 [Python 教程](/lang/python/)。