Python Jinja2 模板引擎详解

760 阅读4分钟

Jinja2 是一个功能强大且灵活的 Python 模板引擎,由 Armin Ronacher 开发。它被广泛应用于 Web 开发框架中,如 Flask、Django 等。Jinja2 的设计理念是简单、快速和安全,它提供了丰富的模板语法,让开发者能够轻松地生成动态内容。

主要特性

  • 高性能:编译后的模板执行速度快
  • 灵活性:支持继承、包含、宏等高级功能
  • 易用性:语法简洁,学习曲线平缓
  • 可扩展性:支持自定义过滤器、函数和测试

快速开始

步骤1:安装Jinja2

pip install Jinja2

步骤2:创建Python脚本

创建一个名为 hello_jinja.py 的文件:

from jinja2 import Template

# 定义一个简单的模板
template_string = """
<!DOCTYPE html>
<html>
<head>
    <title>{{ title }}</title>
</head>
<body>
    <h1>{{ greeting }}</h1>
    
    {% if user %}
        <p>欢迎,{{ user.name }}!</p>
        <p>您的年龄是:{{ user.age }}岁</p>
        
        {% if user.age >= 18 %}
            <p style="color: green;">您已成年</p>
        {% else %}
            <p style="color: orange;">您未成年</p>
        {% endif %}
    {% else %}
        <p>请先登录</p>
    {% endif %}
    
    <h2>您的爱好:</h2>
    <ul>
    {% for hobby in hobbies %}
        <li>{{ hobby }}</li>
    {% else %}
        <li>暂无爱好</li>
    {% endfor %}
    </ul>
    
    <p>当前时间:{{ current_time }}</p>
</body>
</html>
"""

# 创建模板对象
template = Template(template_string)

# 准备数据
data = {
    'title': '我的第一个Jinja2页面',
    'greeting': 'Hello, Jinja2!',
    'user': {
        'name': '张三',
        'age': 25
    },
    'hobbies': ['编程', '阅读', '旅行', '摄影'],
    'current_time': '2024-01-15 14:30:00'
}

# 渲染模板
html_output = template.render(data)

# 保存到文件
with open('output.html', 'w', encoding='utf-8') as f:
    f.write(html_output)

print("HTML文件已生成:output.html")
print("请在浏览器中打开查看效果")

步骤3:运行脚本

python hello_jinja.py

步骤4:查看结果

运行后会在当前目录生成 output.html 文件,用浏览器打开会看到:

<!DOCTYPE html>
<html>
<head>
    <title>我的第一个Jinja2页面</title>
</head>
<body>
    <h1>Hello, Jinja2!</h1>
    
    <p>欢迎,张三!</p>
    <p>您的年龄是:25岁</p>
    
    <p style="color: green;">您已成年</p>
    
    <h2>您的爱好:</h2>
    <ul>
        <li>编程</li>
        <li>阅读</li>
        <li>旅行</li>
        <li>摄影</li>
    </ul>
    
    <p>当前时间:2024-01-15 14:30:00</p>
</body>
</html>

基本语法

Jinja2 语法总览

Jinja2 有三种主要的语法标记:

  1. 变量输出{{ 变量名 }}
  2. 控制语句{% 语句 %}
  3. 注释{# 注释内容 #}

详细语法说明

1. 变量输出 {{ }}

{{ variable }}              {# 输出变量 #}
{{ user.name }}             {# 输出对象属性 #}
{{ user['age'] }}           {# 输出字典值 #}
{{ list[0] }}               {# 输出列表元素 #}
{{ "Hello " + name }}       {# 字符串拼接 #}

2. 控制语句 {% %}

条件判断

{% if condition %}
    内容
{% elif condition2 %}
    内容
{% else %}
    内容
{% endif %}

循环遍历

{% for item in items %}
    内容
{% else %}
    空列表时的内容
{% endfor %}

模板继承

{% extends "base.html" %}           {# 继承模板 #}
{% block name %}内容{% endblock %}   {# 定义块 #}

包含文件

{% include 'file.html' %}           {# 包含其他模板 #}

导入宏

{% from 'file.html' import macro %} {# 导入宏 #}
{% macro name() %}内容{% endmacro %} {# 定义宏 #}

设置变量

{% set name = value %}              {# 设置变量 #}

3. 注释 {# #}

{# 这是注释,不会输出到结果中 #}
{{ name }} {# 行内注释 #}

4. 过滤器

{{ value|filter }}                  {# 单个过滤器 #}
{{ value|filter1|filter2 }}         {# 多个过滤器 #}
{{ value|filter(arg) }}             {# 带参数的过滤器 #}

5. 测试

{% if value is test %}              {# 使用测试 #}
{% if value is defined %}           {# 检查变量是否存在 #}
{% if value is none %}              {# 检查是否为None #}
{% if value is string %}            {# 检查是否为字符串 #}
{% if value is number %}            {# 检查是否为数字 #}

常用过滤器

{{ name|title }}                    {# 首字母大写 #}
{{ text|truncate(50) }}             {# 截断文本 #}
{{ list|length }}                   {# 获取长度 #}
{{ value|default('N/A') }}          {# 默认值 #}
{{ html|safe }}                     {# 不转义HTML #}
{{ text|striptags }}                {# 移除HTML标签 #}
{{ text|upper }}                    {# 转大写 #}
{{ text|lower }}                    {# 转小写 #}
{{ list|join(', ') }}               {# 列表转字符串 #}

常用测试

{% if value is defined %}           {# 变量已定义 #}
{% if value is none %}              {# 值为None #}
{% if value is string %}            {# 是字符串 #}
{% if value is number %}            {# 是数字 #}
{% if value is sequence %}          {# 是序列 #}
{% if value is mapping %}           {# 是字典 #}
{% if value is even %}              {# 是偶数 #}
{% if value is odd %}               {# 是奇数 #}
{% if value is divisibleby(3) %}    {# 能被3整除 #}

模板继承

模板继承让你可以创建一个基础模板,然后其他页面继承这个基础模板。这样可以避免重复代码。

关键概念{% block Ident %} XXX {% endblock %} 是占位符,子模板可以替换中间的内容 XXX

基础模板 (base.html)

<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}默认标题{% endblock %}</title>
</head>
<body>
    <main>
        {% block content %}
        <p>这是默认内容</p>
        {% endblock %}
    </main>
</body>
</html>

子模板 (page.html)

{% extends "base.html" %}

{% block title %}关于我们{% endblock %}

{% block content %}
<h2>关于我们</h2>
<p>这是一个关于页面</p>
{% endblock %}

结果:子模板会继承基础模板的结构,只替换 {% block %} 中的内容。

包含其他模板

{% include 'header.html' %}
<p>页面内容</p>
{% include 'footer.html' %}

宏就像是一个可重用的模板片段,类似于函数。你可以定义一次,然后在多个地方使用。

示例

首先创建一个宏文件 macros.html

{# 定义一个用户卡片宏 #}
{% macro user_card(name, email) %}
<div class="card">
    <h3>{{ name }}</h3>
    <p>{{ email }}</p>
</div>
{% endmacro %}

然后在其他模板中导入并使用:

{% from 'macros.html' import user_card %}

{# 使用宏 #}
{{ user_card('张三', 'zhangsan@example.com') }}
{{ user_card('李四', 'lisi@example.com') }}

结果:会生成两个相同的用户卡片结构,但内容不同。

自定义过滤器

除了使用内置过滤器,你还可以创建自己的过滤器。下面是一个例子

from jinja2 import Environment

# 定义过滤器函数
def reverse_filter(text):
    """反转字符串"""
    return text[::-1]

def currency_filter(amount):
    """格式化货币"""
    return f"¥{amount:.2f}"

def truncate_words_filter(text, length=10):
    """按单词截断文本"""
    words = text.split()
    if len(words) <= length:
        return text
    return ' '.join(words[:length]) + '...'

# 创建环境并注册过滤器
env = Environment()
env.filters['reverse'] = reverse_filter
env.filters['currency'] = currency_filter
env.filters['truncate_words'] = truncate_words_filter

# 使用模板
template = env.from_string("""
价格: {{ price|currency }}
反转: {{ text|reverse }}
摘要: {{ long_text|truncate_words(5) }}
""")

# 渲染模板
result = template.render(
    price=99.99,
    text="Hello World",
    long_text="这是一个很长的文本,包含很多单词,需要被截断"
)

print(result)