Jinja2是一个功能强大、灵活的模板引擎,专门为Python设计。它允许你将动态数据嵌入到HTML模板中。
使用双花括号 {{ }} 来输出变量的值:
<!-- templates/user.html -->
<h1>欢迎,@{{ username }}!</h1>
<p>您的邮箱是:{{ email }}</p>
<p>登录时间:{{ login_time.strftime('%Y-%m-%d %H:%M') }}</p>
@
Jinja2支持多种表达式:
<!-- 数学运算 -->
<p>商品数量:{{ quantity }}</p>
<p>总价:{{ price * quantity }} 元</p>
<p>折扣价:{{ price * 0.8 if is_vip else price }}</p>
<!-- 字符串操作 -->
<p>用户名大写:{{ username.upper() }}</p>
<p>简介:{{ bio[:100] + '...' if bio|length > 100 else bio }}</p>
使用 {# #} 添加注释:
{# 这是一个Jinja2注释,不会输出到HTML #}
<div class="user-profile">
{# 显示用户头像 #}
<img src="{{ avatar_url }}" alt="{{ username }}">
{#
多行注释示例
这里可以写详细的说明
#}
<h2>{{ full_name }}</h2>
</div>
使用 {% if %}、{% elif %}、{% else %}:
<!-- 简单的if语句 -->
{% if user.is_authenticated %}
<p>欢迎回来,{{ user.name }}!</p>
<a href="/logout">退出登录</a>
{% else %}
<p>请先<a href="/login">登录</a></p>
{% endif %}
<!-- 复杂的条件判断 -->
{% if score >= 90 %}
<span class="badge bg-success">优秀</span>
{% elif score >= 60 %}
<span class="badge bg-warning">及格</span>
{% else %}
<span class="badge bg-danger">不及格</span>
{% endif %}
使用 {% for %} 循环:
<!-- 基本的for循环 -->
<ul>
{% for item in items %}
<li>{{ item.name }} - ¥{{ item.price }}</li>
{% endfor %}
</ul>
<!-- 使用loop变量 -->
<table class="table">
<thead>
<tr>
<th>#</th>
<th>姓名</th>
<th>成绩</th>
</tr>
</thead>
<tbody>
{% for student in students %}
<tr class="{{ 'table-success' if loop.index is divisibleby 2 else '' }}">
<td>{{ loop.index }}</td>
<td>{{ student.name }}</td>
<td>{{ student.score }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- 遍历字典 -->
<dl>
{% for key, value in config.items() %}
<dt>{{ key }}</dt>
<dd>{{ value }}</dd>
{% endfor %}
</dl>
loop.index(从1开始)、loop.index0(从0开始)、
loop.first、loop.last、loop.length等
过滤器用于修改变量的输出,使用管道符号 |:
<!-- 字符串过滤器 -->
<p>原始:{{ text }}</p>
<p>大写:{{ text|upper }}</p>
<p>小写:{{ text|lower }}</p>
<p>标题化:{{ text|title }}</p>
<p>首字母大写:{{ text|capitalize }}</p>
<p>截断:{{ text|truncate(50) }}</p>
<!-- 列表过滤器 -->
<p>列表长度:{{ items|length }}</p>
<p>第一个元素:{{ items|first }}</p>
<p>最后一个元素:{{ items|last }}</p>
<p>排序:{{ items|sort }}</p>
<p>连接:{{ items|join(', ') }}</p>
<!-- 其他常用过滤器 -->
<p>默认值:{{ value|default('暂无数据') }}</p>
<p>四舍五入:{{ 3.14159|round(2) }}</p>
<p>绝对值:{{ -5|abs }}</p>
<p>安全HTML:{{ html_content|safe }}</p>
<p>转义HTML:{{ user_input|e }}</p>
可以连续使用多个过滤器:
<p>{{ content|truncate(100)|striptags|safe }}</p>
<p>{{ datetime|date('Y-m-d H:i:s') }}</p>
<p>{{ items|selectattr('active')|list|length }} 个活跃项目</p>
模板继承是Jinja2最强大的功能之一,可以创建基础模板,然后在子模板中扩展。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}默认标题{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
{% block head_extra %}{% endblock %}
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="/">我的网站</a>
{% block navbar %}{% endblock %}
</div>
</nav>
<div class="container mt-4">
{% block content %}
<!-- 默认内容 -->
<p>这里是默认内容。</p>
{% endblock %}
</div>
<footer class="mt-5 py-3 bg-light">
<div class="container text-center">
{% block footer %}
<p>© 2023 我的网站。保留所有权利。</p>
{% endblock %}
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
{% block scripts %}{% endblock %}
</body>
</html>
{% extends "base.html" %}
{% block title %}首页 - 我的网站{% endblock %}
{% block head_extra %}
<link rel="stylesheet" href="/css/home.css">
{% endblock %}
{% block navbar %}
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link active" href="/">首页</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/about">关于</a>
</li>
</ul>
{% endblock %}
{% block content %}
<h1 class="mb-4">欢迎来到我的网站!</h1>
{{ super() }} {# 调用父模板中的content块内容 #}
<div class="row">
{% for product in products %}
<div class="col-md-4 mb-3">
<div class="card">
<div class="card-body">
<h5 class="card-title">{{ product.name }}</h5>
<p class="card-text">{{ product.description }}</p>
<span class="badge bg-primary">¥{{ product.price }}</span>
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
{% block scripts %}
<script src="/js/home.js"></script>
{% endblock %}
宏类似于函数,可以重用HTML代码片段。
{# 在macros.html中定义宏 #}
{% macro render_product_card(product) %}
<div class="card product-card">
<img src="{{ product.image_url or '/images/default.png' }}"
class="card-img-top"
alt="{{ product.name }}">
<div class="card-body">
<h5 class="card-title">{{ product.name }}</h5>
<p class="card-text">{{ product.description|truncate(100) }}</p>
<div class="d-flex justify-content-between align-items-center">
<span class="h5 text-danger">¥{{ product.price }}</span>
<a href="/product/{{ product.id }}" class="btn btn-primary">查看详情</a>
</div>
</div>
</div>
{% endmacro %}
{% macro render_user_avatar(user, size='md') %}
{% set size_class = {
'sm': 'avatar-sm',
'md': 'avatar-md',
'lg': 'avatar-lg'
} %}
<div class="avatar {{ size_class.get(size, 'avatar-md') }}">
<img src="{{ user.avatar }}" alt="{{ user.username }}">
{% if user.is_online %}
<span class="online-dot"></span>
{% endif %}
</div>
{% endmacro %}
{# 在模板中导入宏 #}
{% from 'macros.html' import render_product_card, render_user_avatar %}
<!-- 使用宏 -->
<div class="user-profile">
{{ render_user_avatar(current_user, 'lg') }}
<h2>{{ current_user.username }}</h2>
</div>
<div class="row">
{% for product in products %}
<div class="col-md-4 mb-4">
{{ render_product_card(product) }}
</div>
{% endfor %}
</div>
|e 过滤器include 引入公共组件{% extends "base.html" %}
{% block title %}用户管理{% endblock %}
{% block content %}
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>用户列表</h1>
<a href="/users/add" class="btn btn-primary">添加用户</a>
</div>
{% if users %}
<table class="table table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
<th>角色</th>
<th>注册时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.id }}</td>
<td>
<img src="{{ user.avatar }}" class="rounded-circle me-2" width="30">
{{ user.username }}
{% if user.is_vip %}
<span class="badge bg-warning ms-2">VIP</span>
{% endif %}
</td>
<td>{{ user.email }}</td>
<td>
<span class="badge {{ 'bg-info' if user.role == 'admin' else 'bg-secondary' }}">
{{ user.role|title }}
</span>
</td>
<td>{{ user.created_at|date('Y-m-d') }}</td>
<td>
<a href="/users/{{ user.id }}" class="btn btn-sm btn-outline-primary">查看</a>
<a href="/users/{{ user.id }}/edit" class="btn btn-sm btn-outline-warning">编辑</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- 分页 -->
{% if pagination %}
<nav>
<ul class="pagination justify-content-center">
{% if pagination.has_prev %}
<li class="page-item">
<a class="page-link" href="?page={{ pagination.prev_num }}">上一页</a>
</li>
{% endif %}
{% for page in pagination.iter_pages() %}
{% if page %}
<li class="page-item {{ 'active' if page == pagination.page else '' }}">
<a class="page-link" href="?page={{ page }}">{{ page }}</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">...</span>
</li>
{% endif %}
{% endfor %}
{% if pagination.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ pagination.next_num }}">下一页</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
暂无用户数据。
</div>
{% endif %}
{% endblock %}
通过本教程,你已经掌握了Jinja2模板引擎的核心功能:
{{ }} 输出动态内容{% if %}、{% for %} 等| 修改变量输出{% extends %} 和 {% block %}