Appearance
模板把"页面结构"与"动态数据"分离: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
控制语句需用 {% 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)
父模板定义整体结构({% block %} 占位),子模板 {% 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.7python
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教程-与用户交互 继续讲)。
实践建议
- 模板统一放
templates/、静态资源放static/,不要混放 - 公共部分(导航栏/页头/页脚)优先用继承 + 包含,改一处全站生效
- 自定义函数/过滤器注册为全局对象,避免每个视图函数重复注入
- 动手完成课后练习:父模板 + 子模板 + 宏指令做一个多页面小站
一句话总结
模板把结构和数据分开:Jinja2 记牢 {{ }} 输出、{% %} 控制、{# #} 注释三种标记;公共页面用 {% extends %} + {% block %} 继承或 {% include %} 包含,重复 HTML 用 {% macro %} 封装。