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 如何找到和读取模板文件。

创建模板环境

env = Environment(loader=FileSystemLoader('templates'))
  • env:这是一个 Environment 类的实例,代表 Jinja2 模板的全局环境。
    • Environment 的实例化:通过 Environment() 创建的 env 对象,负责管理和存储所有模板相关的配置和信息。
  • loader=FileSystemLoader('templates')
    • loader 参数Environment 类的一个关键参数,用于指定模板文件的加载器。在这里,我们使用 FileSystemLoader 加载器。
    • FileSystemLoader('templates'):指定了模板文件所在的目录 'templates',表示所有的模板文件都存放在这个文件夹中。
      • 作用:当你在后续代码中请求模板时,Jinja2 会在 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':指定了要加载的模板文件的名称。
      • 路径查找:Jinja2 会在 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 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>&copy; 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> 表单元素。这个宏接收多个参数,如 namevaluetypeplaceholder
  • 宏的使用:通过调用 {{ 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_nameuser_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 页面。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐