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 中的一种加载器,用于从文件系统中的指定目录加载模板文件。
FileSystemLoader
是其中最常用的一种,适用于从文件系统加载模板。加载器的作用是告诉 Jinja2 如何找到和读取模板文件。env = Environment(loader=FileSystemLoader('templates'))
env
:这是一个 Environment
类的实例,代表 Jinja2 模板的全局环境。
Environment
的实例化:通过 Environment()
创建的 env
对象,负责管理和存储所有模板相关的配置和信息。loader=FileSystemLoader('templates')
:
loader
参数:Environment
类的一个关键参数,用于指定模板文件的加载器。在这里,我们使用 FileSystemLoader
加载器。FileSystemLoader('templates')
:指定了模板文件所在的目录 'templates'
,表示所有的模板文件都存放在这个文件夹中。
templates
目录中查找相应的模板文件。代码扩展:
可以在 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'
:指定了要加载的模板文件的名称。
FileSystemLoader
指定的目录(即 'templates'
文件夹)中查找名为 my_template.html
的文件,并将其作为模板进行加载。get_template()
方法返回一个 Template
对象,代表该模板文件,可以使用它进行渲染。代码扩展:
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 Infotitle>
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 Infotitle>
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 Pagetitle>
head>
<body>
<h1>Hello, {{ user_name }}!h1>
<p>Welcome to our website. Your email is: {{ user_email }}p>
body>
html>
from jinja2 import Environment, FileSystemLoader
# 创建 Jinja2 环境,指定模板文件的路径
env = Environment(loader=FileSystemLoader('templates'))
# 加载模板文件
template = env.get_template('welcome.html')
# 定义模板中需要插入的变量
context = {
'user_name': 'Alice',
'user_email': '[email protected]'
}
# 渲染模板并生成最终的 HTML
rendered_html = template.render(context)
print(rendered_html)
{{ user_name }}
会在最终的 HTML 中被替换为 Alice
,{{ user_email }}
会被替换为 [email protected]
。<h1>Hello, Alice!h1>
<p>Welcome to our website. Your email is: [email protected]p>
Jinja2 支持常见的控制结构,如条件语句 (if
)、循环 (for
),这使得模板能够根据条件或数据动态生成不同的内容。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>User Dashboardtitle>
head>
<body>
<h1>Dashboardh1>
{% 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 Listtitle>
head>
<body>
<h1>Available Productsh1>
<ul>
{% for product in products %}
<li>{{ product.name }} - ${{ product.price }}li>
{% else %}
<li>No products availableli>
{% endfor %}
ul>
body>
html>
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 - $1200li>
<li>Smartphone - $800li>
<li>Tablet - $450li>
ul>
Jinja2 提供了一系列内置过滤器,用于在模板中对变量进行格式化和处理。过滤器使用管道符号 |
进行应用,类似于 Unix 的管道操作。
upper
:将字符串转换为大写。lower
:将字符串转换为小写。date
:格式化日期。length
:获取列表或字符串的长度。DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>User Profiletitle>
head>
<body>
<h1>User Profileh1>
<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>
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 Websiteh1>
<nav>
<a href="/">Homea>
<a href="/about">Abouta>
<a href="/contact">Contacta>
nav>
header>
<main>
{% block content %}{% endblock %}
main>
<footer>
<p>© 2024 My Websitep>
footer>
body>
html>
home.html
):{% extends "base.html" %}
{% block title %}Home - My Website{% endblock %}
{% block content %}
<h2>Welcome to the Home Pageh2>
<p>This is the main content of the home page.p>
{% endblock %}
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 Websitetitle>
<h2>Welcome to the Home Pageh2>
<p>This is the main content of the home page.p>
宏类似于 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">Loginbutton>
form>
template = env.get_template('form.html')
rendered_html = template.render()
print(rendered_html)
{% macro render_input %}
:定义了一个名为 render_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">Loginbutton>
在 FastAPI 中使用 Jinja2 渲染和返回动态页面是一种常见的做法,尤其是在需要生成动态 HTML 内容时。以下是详细步骤和代码示例,展示如何在 FastAPI 中集成 Jinja2 并返回动态页面。
首先,确保安装了 FastAPI 和 Jinja2 相关的依赖包:
pip install fastapi jinja2 uvicorn
假设你的项目结构如下:
.
├── app.py
├── templates
│ └── index.html
app.py
: 主应用程序文件。templates
: 存放 Jinja2 模板文件的目录。index.html
: 一个示例模板文件。在 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>Welcometitle>
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
的值显示不同的内容。在终端中启动 Uvicorn 服务器以运行 FastAPI 应用:
uvicorn app:app --reload
然后访问 http://127.0.0.1:8000/
,你将看到渲染后的 HTML 页面。