Skip to content

模板把"页面结构"与"动态数据"分离:Jinja2 模板引擎负责将数据注入模板生成最终 HTML。本章掌握 Jinja2 三大语法(表达式/控制/注释)、模板的包含与继承、宏指令、全局对象与过滤器,并用 Flask-Bootstrap 快速搭建带导航栏与响应式布局的页面。

模板与 Jinja2 简介

  • 模板:通用的布局/结构,作用 = 内容注入 + 页面继承与包含,保持一致性、减少重复
  • Jinja2:Python 模板引擎,将动态数据嵌入静态模板,输出 HTML/XML/JSON 等
  • 约定:模板放 templates/ 目录,静态资源放 static/ 目录
  • 思路:每写一个模板页面,都要写对应的视图函数,否则页面无法被浏览
python
from flask import Flask, render_template

@app.route('/value')
def value(username=None):
    return render_template('value.html')

Jinja2 三大基本语法

语法类型用途
{% ... %}控制语句结构控制、定义模板与变量(如 {% if %} {% for %}
{{ ... }}表达式语句输出变量、调用宏/函数
{# ... #}注释语句代码注解,不输出到最终页面

表达式:注入变量

python
@app.route('/value')
@app.route('/value/<string:username>')
def value(username=None):
    return render_template('value.html', username=username)
html
<!-- 表达式语句中可用 or 给变量默认值 -->
<h1>{{ username or '游客' }}</h1>

控制:if / for

控制语句需用 &#123;% endxxx %} 闭合,语法与 Python 相似:

html
{% if num <= 0 %}
    <h2>文章数量为0,没有内容可展示。</h2>
{% else %}
    {% for article in articles %}
        <h2>{{ article.title }}</h2>
        <p>{{ article.content }}</p>
    {% endfor %}
{% endif %}
python
@app.route('/control/<int:num>')
def control(num=0):
    articles = [{'title': '文章%d标题' % i, 'content': '内容'} for i in range(1, num+1)]
    return render_template('control.html', num=num, articles=articles)

模板的包含与继承

包含(include)

将一个模板嵌入另一个模板,实现模块化复用:

html
{% include 'header.html' %}   <!-- 在任意页面引用公共页头 -->

继承(extends + block)

父模板定义整体结构(&#123;% block %} 占位),子模板 &#123;% extends %} 继承并填充/覆盖:

html
<!-- base.html 父模板 -->
<html><head><title>{% block title %}Default{% endblock %}</title></head>
<body><header>{% block header %}{% endblock %}</header>
<main>{% block content %}{% endblock %}</main></body></html>
html
<!-- 子模板 -->
{% extends 'base.html' %}
{% block title %}Page Title{% endblock %}
{% block content %}<p>This is the main content.</p>{% endblock %}

继承把"页面结构"和"特定内容"分开,公共部分只改一处即可全站生效。

宏指令(macro)

宏类似"模板函数",可定义在单独文件再导入,避免重复写 HTML:

html
<!-- macro_define.html -->
{% macro article_item(title, content) %}
    <h2>{{ title }}</h2><p>{{ content }}</p>
{% endmacro %}
{% macro article_list(articles, num) %}
    {% if num <= 0 %}<h2>暂无内容</h2>
    {% else %}{% for a in articles %}{{ article_item(a.title, a.content) }}{% endfor %}{% endif %}
{% endmacro %}
html
<!-- 使用:import 后像函数一样调用 -->
{% import 'macro_define.html' as macro %}
{{ macro.article_list(articles, num) }}
<a href="{{ url_for('macro', num=num_next) }}">+1</a>   <!-- url_for 生成路由 URL -->

注册全局对象与过滤器

  • 全局对象app.add_template_global(函数, '别名')——让函数在继承模板中直接可用,不必每个视图函数都注入
  • 变量过滤器app.add_template_filter(函数)——在模板中用 | 对变量格式化
python
import time
def convert_time(t):
    return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(t))
app.add_template_filter(convert_time)
html
<p>{{ 1572497288.095447 | convert_time }}</p>

Flask-Bootstrap

Flask-Bootstrap 提供基础模板(含资源引用与快捷宏),省去从零配置:

bash
pip install flask-bootstrap==3.3.7
python
from flask_bootstrap import Bootstrap
bootstrap = Bootstrap()
bootstrap.init_app(app)
# 使用本地资源,禁用 CDN(离线可用)
cdns = app.extensions['bootstrap']['cdns']
cdns['bootstrap'] = cdns['local']; cdns['jquery'] = cdns['local']

模板继承 bootstrap/base.html 并重写常用区块:

区块解释
title网页标题
styles样式表标签区块
navbar导航栏区块
content网页主体区块
scripts页面尾部脚本区块
html
{% extends 'bootstrap/base.html' %}
{% block title %}Flask Bootstrap{% endblock %}
{% block navbar %}{% include 'include_nav.html' %}{% endblock %}
{% block content %}<div class="container">...</div>{% endblock %}

Flask-Bootstrap 还提供 flask_wtf 表单生成宏Flask教程-与用户交互 继续讲)。

实践建议

  1. 模板统一放 templates/、静态资源放 static/,不要混放
  2. 公共部分(导航栏/页头/页脚)优先用继承 + 包含,改一处全站生效
  3. 自定义函数/过滤器注册为全局对象,避免每个视图函数重复注入
  4. 动手完成课后练习:父模板 + 子模板 + 宏指令做一个多页面小站

一句话总结

模板把结构和数据分开:Jinja2 记牢 &#123;&#123; }} 输出、&#123;% %} 控制、&#123;# #} 注释三种标记;公共页面用 &#123;% extends %} + &#123;% block %} 继承或 &#123;% include %} 包含,重复 HTML 用 &#123;% macro %} 封装。

相关概念