Jinja2 是一个广泛使用的 Python 模板引擎,它允许你使用模板语法生成动态的 HTML、XML、或者其他文本文件。Jinja2 是 Flask 和 FastAPI 等 Web 框架中默认的模板引擎,但它也可以在任何 Python 项目中独立使用。
部署与使用
安装
pip install jinja2
导入模块和类
from jinja2 import Environment, FileSystemLoader
jinja2
是 Jinja2 模板引擎的 Python 模块。Environment
是 Jinja2 的核心类之一,负责管理模板的全局配置、变量的全局设置,以及模板文件的加载方式。Environment
的作用:这个类用于创建模板环境,管理模板的全局配置,如自动转义、缓存设置、扩展的使用等。在Environment
对象中,可以定义如何加载模板、模板的编码格式、全局变量和过滤器等。
FileSystemLoader
是 Jinja2 中的一种加载器,用于从文件系统中的指定目录加载模板文件。- 加载器的作用:Jinja2 提供了多种加载器,
FileSystemLoader
是其中最常用的一种,适用于从文件系统加载模板。加载器的作用是告诉 Jinja2 如何找到和读取模板文件。
- 加载器的作用:Jinja2 提供了多种加载器,
创建模板环境
env = Environment(loader=FileSystemLoader('templates'))
env
:这是一个Environment
类的实例,代表 Jinja2 模板的全局环境。Environment
的实例化:通过Environment()
创建的env
对象,负责管理和存储所有模板相关的配置和信息。
loader=FileSystemLoader('templates')
:loader
参数:Environment
类的一个关键参数,用于指定模板文件的加载器。在这里,我们使用FileSystemLoader
加载器。FileSystemLoader('templates')
:指定了模板文件所在的目录'templates'
,表示所有的模板文件都存放在这个文件夹中。- 作用:当你在后续代码中请求模板时,Jinja2 会在
templates
目录中查找相应的模板文件。
- 作用:当你在后续代码中请求模板时,Jinja2 会在
代码扩展:
可以在 Environment
中传递更多的配置参数,例如:
env = Environment(
loader=FileSystemLoader('templates'),
autoescape=True, # 自动转义 HTML 特殊字符,防止 XSS 攻击
cache_size=50, # 设置缓存大小,提高模板加载的效率
trim_blocks=True, # 去除块级标签中的空白行
lstrip_blocks=True # 去除块级标签前的空白字符
)
加载模板
template = env.get_template('my_template.html')
template
:这是一个Template
对象,表示加载的模板文件。- 作用:
template
对象持有了my_template.html
模板的内容,可以使用这个对象进行渲染操作。
- 作用:
env.get_template('my_template.html')
:get_template
方法:这是Environment
类的一个方法,用于加载指定的模板文件。- 参数
'my_template.html'
:指定了要加载的模板文件的名称。- 路径查找:Jinja2 会在
FileSystemLoader
指定的目录(即'templates'
文件夹)中查找名为my_template.html
的文件,并将其作为模板进行加载。 - 返回值:
get_template()
方法返回一个Template
对象,代表该模板文件,可以使用它进行渲染。
- 路径查找:Jinja2 会在
代码扩展:
- 处理不存在的模板文件:
- 如果模板文件不存在,
get_template
会抛出TemplateNotFound
异常。你可以通过try...except
结构来处理这个错误。try: template = env.get_template('non_existent_template.html') except TemplateNotFound: print("Template file not found!")
- 如果模板文件不存在,
渲染模板
rendered_html = template.render(user_name="Alice", user_is_admin=True)
-
rendered_html
:这是一个字符串变量,存储了渲染后的 HTML 或其他格式的内容。- 作用:
rendered_html
是模板渲染的最终结果,即在模板中填充变量后的完整内容,通常是 HTML 代码,但也可以是任何字符串形式的内容(例如纯文本、JSON 等)。
- 作用:
-
template.render()
:render
方法:这是Template
类的一个方法,用于将模板中的占位符替换为实际的值,从而生成最终的输出。- 作用:
render
方法会解析模板中的变量和控制结构,将传递的实际值插入到模板中,生成渲染后的内容。
-
参数传递:
user_name="Alice"
:向模板传递一个名为user_name
的变量,值为"Alice"
。- 作用:模板中所有引用
user_name
变量的地方都会被替换为"Alice"
。
- 作用:模板中所有引用
user_is_admin=True
:向模板传递一个名为user_is_admin
的布尔变量,值为True
。- 作用:模板中所有引用
user_is_admin
变量的地方都会被替换为True
,并且可以用于控制结构(如条件语句)来决定是否显示特定内容。
- 作用:模板中所有引用
代码扩展:
假设我们有以下模板 my_template.html
:
<!DOCTYPE html>
<html>
<head>
<title>User Info</title>
</head>
<body>
<h1>Welcome, {{ user_name }}!</h1>
{% if user_is_admin %}
<p>You have admin privileges.</p>
{% else %}
<p>You are a regular user.</p>
{% endif %}
</body>
</html>
当我们使用 template.render(user_name="Alice", user_is_admin=True)
渲染这个模板时,最终生成的 rendered_html
将是:
<!DOCTYPE html>
<html>
<head>
<title>User Info</title>
</head>
<body>
<h1>Welcome, Alice!</h1>
<p>You have admin privileges.</p>
</body>
</html>
使用多变量渲染
除了单一变量传递外,render
方法可以接受任意数量的键值对参数:
rendered_html = template.render(
user_name="Bob",
user_is_admin=False,
greeting="Hello",
notifications=5
)
对应的模板可能会这样写:
<h1>{{ greeting }}, {{ user_name }}!</h1>
{% if notifications > 0 %}
<p>You have {{ notifications }} new notifications.</p>
{% else %}
<p>No new notifications.</p>
{% endif %}
渲染结果会是:
<h1>Hello, Bob!</h1>
<p>You have 5 new notifications.</p>
模板
变量插值
Jinja2 允许在模板中使用双花括号 {{ }}
语法插入变量。变量插值是 Jinja2 最基本的功能之一,通常用于动态生成内容。
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Welcome Page</title>
</head>
<body>
<h1>Hello, {{ user_name }}!</h1>
<p>Welcome to our website. Your email is: {{ user_email }}</p>
</body>
</html>
Python 代码:
from jinja2 import Environment, FileSystemLoader
# 创建 Jinja2 环境,指定模板文件的路径
env = Environment(loader=FileSystemLoader('templates'))
# 加载模板文件
template = env.get_template('welcome.html')
# 定义模板中需要插入的变量
context = {
'user_name': 'Alice',
'user_email': 'alice@example.com'
}
# 渲染模板并生成最终的 HTML
rendered_html = template.render(context)
print(rendered_html)
解释:
- 变量插值:
{{ user_name }}
会在最终的 HTML 中被替换为Alice
,{{ user_email }}
会被替换为alice@example.com
。 - 输出:最终生成的 HTML 将是:
<h1>Hello, Alice!</h1> <p>Welcome to our website. Your email is: alice@example.com</p>
控制结构
Jinja2 支持常见的控制结构,如条件语句 (if
)、循环 (for
),这使得模板能够根据条件或数据动态生成不同的内容。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>User Dashboard</title>
</head>
<body>
<h1>Dashboard</h1>
{% if user_is_admin %}
<p>Welcome, admin! You have full access to the system.</p>
{% else %}
<p>Welcome, user! Your access is limited.</p>
{% endif %}
</body>
</html>
context = {
'user_is_admin': True
}
rendered_html = template.render(context)
print(rendered_html)
循环语句示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Product List</title>
</head>
<body>
<h1>Available Products</h1>
<ul>
{% for product in products %}
<li>{{ product.name }} - ${{ product.price }}</li>
{% else %}
<li>No products available</li>
{% endfor %}
</ul>
</body>
</html>
Python 代码:
context = {
'products': [
{'name': 'Laptop', 'price': 1200},
{'name': 'Smartphone', 'price': 800},
{'name': 'Tablet', 'price': 450}
]
}
rendered_html = template.render(context)
print(rendered_html)
解释:
- 条件语句:
{% if user_is_admin %}
判断用户是否为管理员,输出相应的欢迎信息。 - 循环:
{% for product in products %}
遍历products
列表,生成每个产品的列表项。如果products
列表为空,{% else %}
分支会显示 “No products available”。 - 输出:
<ul> <li>Laptop - $1200</li> <li>Smartphone - $800</li> <li>Tablet - $450</li> </ul>
过滤器
Jinja2 提供了一系列内置过滤器,用于在模板中对变量进行格式化和处理。过滤器使用管道符号 |
进行应用,类似于 Unix 的管道操作。
常见过滤器示例:
upper
:将字符串转换为大写。lower
:将字符串转换为小写。date
:格式化日期。length
:获取列表或字符串的长度。
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>User Profile</title>
</head>
<body>
<h1>User Profile</h1>
<p>Name: {{ user_name | upper }}</p>
<p>Member since: {{ join_date | date("Y-m-d") }}</p>
<p>You have {{ notifications | length }} new notifications.</p>
</body>
</html>
Python 代码:
from datetime import datetime
context = {
'user_name': 'Alice',
'join_date': datetime(2021, 3, 15),
'notifications': ['Message 1', 'Message 2', 'Message 3']
}
rendered_html = template.render(context)
print(rendered_html)
解释:
upper
过滤器:将user_name
转换为大写,即Alice
变为ALICE
。date
过滤器:将join_date
格式化为YYYY-MM-DD
格式,如2021-03-15
。length
过滤器:计算notifications
列表的长度,即显示 “You have 3 new notifications”。
模板继承
模板继承是 Jinja2 的一大特色,它允许你定义一个基础模板,并在其他模板中继承和扩展该基础模板。这有助于保持代码的可维护性和一致性,尤其是在处理大型项目时。
基础模板示例代码 (base.html
):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}My Website{% endblock %}</title>
</head>
<body>
<header>
<h1>My Website</h1>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>© 2024 My Website</p>
</footer>
</body>
</html>
子模板示例代码 (home.html
):
{% extends "base.html" %}
{% block title %}Home - My Website{% endblock %}
{% block content %}
<h2>Welcome to the Home Page</h2>
<p>This is the main content of the home page.</p>
{% endblock %}
Python 代码:
template = env.get_template('home.html')
rendered_html = template.render()
print(rendered_html)
解释:
{% extends "base.html" %}
:home.html
继承了base.html
中定义的结构和样式。{% block title %}
和{% block content %}
:子模板通过block
标签重写了base.html
中的特定部分,如标题和主要内容。- 输出:
<title>Home - My Website</title> <h2>Welcome to the Home Page</h2> <p>This is the main content of the home page.</p>
宏(Macros)
宏类似于 Python 中的函数,用于封装可重用的模板代码。使用宏可以避免重复代码,并简化复杂的模板逻辑。
宏定义与使用示例代码:
{% macro render_input(name, value='', type='text', placeholder='') %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}" placeholder="{{ placeholder }}">
{% endmacro %}
<form>
{{ render_input('username', type='text', placeholder='Enter your username') }}
{{ render_input('password', type='password', placeholder='Enter your password') }}
<button type="submit">Login</button>
</form>
Python 代码:
template = env.get_template('form.html')
rendered_html = template.render()
print(rendered_html)
解释:
{% macro render_input %}
:定义了一个名为render_input
的宏,用于生成<input>
表单元素。这个宏接收多个参数,如name
、value
、type
和placeholder
。- 宏的使用:通过调用
{{ render_input(...) }}
,可以生成自定义的输入框,而不需要重复编写 HTML 代码。 - 输出:
<input type="text" name="username" value="" placeholder="Enter your username"> <input type="password" name="password" value="" placeholder="Enter your password"> <button type="submit">Login</button>
*FastAPI动态页面
在 FastAPI 中使用 Jinja2 渲染和返回动态页面是一种常见的做法,尤其是在需要生成动态 HTML 内容时。以下是详细步骤和代码示例,展示如何在 FastAPI 中集成 Jinja2 并返回动态页面。
安装依赖
首先,确保安装了 FastAPI 和 Jinja2 相关的依赖包:
pip install fastapi jinja2 uvicorn
项目结构
假设你的项目结构如下:
.
├── app.py
├── templates
│ └── index.html
app.py
: 主应用程序文件。templates
: 存放 Jinja2 模板文件的目录。index.html
: 一个示例模板文件。
创建 FastAPI 应用并配置 Jinja2
在 app.py
中设置 FastAPI 应用程序并配置 Jinja2 进行模板渲染:
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse
app = FastAPI()
# 配置 Jinja2 模板引擎
templates = Jinja2Templates(directory="templates")
@app.get("/", response_class=HTMLResponse)
async def read_root(request: Request):
# 渲染 index.html 模板,并传递上下文变量
return templates.TemplateResponse("index.html", {"request": request, "user_name": "Alice", "user_is_admin": True})
Jinja2Templates(directory="templates")
: 这行代码配置了 Jinja2 模板引擎,指定模板文件存放在templates
目录下。@app.get("/", response_class=HTMLResponse)
: 这个装饰器用于定义一个 GET 路由,当用户访问/
时,这个函数会被调用。TemplateResponse("index.html", {"request": request, "user_name": "Alice", "user_is_admin": True})
:TemplateResponse
: 渲染 Jinja2 模板并返回一个 HTML 响应。"index.html"
: 指定要渲染的模板文件。{"request": request, "user_name": "Alice", "user_is_admin": True}
: 传递给模板的上下文数据,request
对象是必须的,它用于模板内部访问 URL 路径等信息,其他变量如user_name
和user_is_admin
可以在模板中使用。
创建模板文件
在 templates
目录下创建 index.html
文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome</title>
</head>
<body>
<h1>Welcome, {{ user_name }}!</h1>
{% if user_is_admin %}
<p>You have admin privileges.</p>
{% else %}
<p>You are a regular user.</p>
{% endif %}
</body>
</html>
{{ user_name }}
: 用于插入user_name
变量的值。{% if user_is_admin %}
: 控制结构,用于根据user_is_admin
的值显示不同的内容。
启动 FastAPI 应用
在终端中启动 Uvicorn 服务器以运行 FastAPI 应用:
uvicorn app:app --reload
然后访问 http://127.0.0.1:8000/
,你将看到渲染后的 HTML 页面。