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. 变量输出 {{ }}
{{ 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)