实训7:基于模板继承创建长征概述页模板文件【附完整代码】

1. Jinja2简介

模板文件是包含静态的HTML文本及描述数据如何插入到HTML文本的动态内容。Flask默认使用的模板引擎是Jinja2,它是一个功能齐全的Python模板引擎,除了设置变量外,还允许我们通过控制结构添加if判断,执行for循环,调用函数等各种方式来控制模板的输出。对于Jinja2来说,模板可以是任何格式的纯文本文件,比如HTML、XML、CSV等。Jinja2的官网截图如图1所示。(https://jinja.palletsprojects.com/en/3.1.x/templates/)为其模板语法使用的介绍文档URL地址。在安装Flask框架时,会自动安装Jinja2模板,如果要在Python环境中单独安装Jinja2,pip命令的语法格式如下:

pip install Jinja2

图1 jinja2官网截图

2. Jinja2语法

Jinja2中的基本语法是:

  1. 控制结构 {% %}:控制结构用于定义模板中控制程序流的模块,如:判断模块(即if/elif/else)、循环模块(for循环)、宏、块(block)、继承(extends)等。控制结构定义在{%…%}模板标签内,有些控制结构还需有对应的结束标签,如: {% if 条件表达式 %} …  {% endif %}。
  2. 变量取值 {{ }}:在Jinja2模板中,通过 {{ 变量名或表达式 }} 的语法来动态获取变量值,变量名必须由字母、数字、下划线(不能以下划线开头)和点组成。与Python中的变量类似,可以通过 “.” 来访问变量的属性,也可以使用“下标”语法([])来获取。如果变量或属性不存在,则会返回一个未定义值。如:{{ student.name }} 或 {{ student[‘name’] }}。
  3. 注释 {# #}:与Python中用“#”来引导注释内容类似,Jinja2规定模板中的注释内容必须包括在{# … #}的模块中。

3. 模板继承

Jinja2中最强大的部分就是模板继承。模板继承允许开发者创建一个基本(骨架)文件,其余文件从该骨架文件继承,然后只针对需要修改的地方进行修改。在Jinja2的骨架文件中,利用block关键字表示其中的内容能够被子模板进行修改,block块的定义语法如下:

{% block 标识符 %} 可被子模块修改的内容区域 {% endblock %}

在子模板中,通过{% extends 父模板名 %} 实现模板的继承,extends关键字告诉模板引擎,该模板继承于其它模板,当模板引擎渲染子模板时,会首先加载父模板。模板继承标签 {% extends 父模板名 %}应该是子模板文件的第一个出现的模板标签。在子模板中,如果需要重写父模板中的某个特定部分,可通过 {% block 标识符 %} 和 {% endblock %} 覆盖父模板的特定部分并在block块的开始标签和结束标签之间给出子模板的具体实现。

例如,图2所示为“致敬长征”项目中的根模板文件cz_base.html的代码截图,在该模板文件中,使用{% block 标识符 %} 和 {% endblock %}定义了6个可被子模板重写的块:title块用于子模块修改页面的标题,links块用于子模板添加新的静态资源链接,header块允许子模板对页面导航栏模块进行自定义设置,main块用于子模块的内容呈现,footer块允许子模板对页面底部导航模块进行自定义设置,jsinclude块用于子模板定义或导入特定的js代码或文件。cz_base.html根模板文件的页面运行效果如图3所示。图2 “致敬长征”项目的根模板文件cz_base.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{% block title %} 长征 {% endblock %}</title>

    {# Bootstrap 的 CSS 文件 #}
    <link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
    <link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap-icons.min.css') }}">
    <link rel="stylesheet" href="{{ url_for('static', filename='css/cz_style.css') }}">

    {% block links %} {% endblock %}
</head>
<body class="d-flex flex-column h-100">
{% block header %}
<header class="p-3 bg-light">
    <div class="container">
        <div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
            <a href="#" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none">
                <img style="width:70px; height:40px;" src="{{ url_for('static', filename='images/cz.jpg') }}">
            </a>

            <ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0 fs-4">
                <li><a href="/" class="nav-link px-2">首页</a></li>
                <li><a href="{{ url_for('video.video_list_page') }}" class="nav-link px-2">影视</a></li>
                <li><a href="{{ url_for('literature.lit_list_page') }}" class="nav-link px-2">文学</a></li>
                <li><a href="{{ url_for('battle.bat_list_page') }}" class="nav-link px-2">战例</a></li>
                <li><a href="{{ url_for('memorial.mem_list_page') }}" class="nav-link px-2">纪念</a></li>
                <li><a href="{{ url_for('hero.hero_list_page') }}" class="nav-link px-2">将领</a></li>
                <li><a href="{{ url_for('history.his_list_page') }}" class="nav-link px-2">信史</a></li>
            </ul>

            <form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3">
                <div role="search">
                    <input type="search" class="form-control form-control-dark"
                           placeholder="搜索..." aria-label="Search">
                </div>
            </form>

            <div class="text-end">
                <a href="{{ url_for('auth.register') }}" class="btn btn-outline-success me-2">注册</a>
                <a href="{{ url_for('auth.login') }}" class="btn btn-outline-danger">登录</a>
            </div>

        </div>
    </div>
</header>
{% endblock %}

<main>
    {% block main %}
    <div class="w-100 my-5 py-5 text-center">
        <h1>这是页面的内容区域</h1>
    </div>
    {% endblock %}
</main>

{% block footer %}
<div class="container">
    <footer class="py-3 my-4">
        <ul class="nav justify-content-center border-bottom pb-3 mb-3">
            <li class="nav-item">
                <a href="#" class="nav-link px-2 text-muted">返回首页</a>
            </li>
            <li class="nav-item"><a href="#" class="nav-link px-2 text-muted">联系我们</a></li>
            <li class="nav-item"><a href="#" class="nav-link px-2 text-muted">友情链接</a></li>
            <li class="nav-item"><a href="#" class="nav-link px-2 text-muted">关于我们</a></li>
        </ul>
        <p class="text-center text-muted">版权所有 &copy;2023 solute_cz</p>
    </footer>
</div>
{% endblock %}

<script src="{{ url_for('static', filename='js/bootstrap.bundle.min.js') }}"></script>

{% block jsinclude %}  {% endblock %}

</body>
</html>

图3 模板文件cz_base.html的页面呈现效果在图2所示的“致敬长征”项目首页cz_index.html中,第一行代码通过{% extends "/cz_base.html" %}声明从cz_base.html继承,并重写了父模板的title、main和jsinclude块,实现本页面的相应功能。图4 “致敬长征”项目首页cz_index.html

{% extends "/cz_base.html" %}

{% block title %} 长征 {% endblock %}

{% block main %}

<!-- 长征•图片轮播 -->
<section id="myCarousel" class="carousel slide" data-bs-ride="carousel">
    <ul class="carousel-indicators">
        {% for v in v_list %}
        {% if loop.first %}
        <li data-bs-target="#myCarousel" data-bs-slide-to="{{loop.index0}}"
            class="active" aria-current="true" aria-label="Slide{{loop.index}}"></li>
        {% else %}
        <li data-bs-target="#myCarousel" data-bs-slide-to="{{loop.index0}}"
            aria-label="Slide{{loop.index}}"></li>
        {% endif %}
        {% endfor %}
    </ul>
    <div class="carousel-inner">
        {% for v in v_list %}
        {% if loop.first %}
        <div class="carousel-item active">
            <img class="d-block w-100" src="{{ url_for('static', filename=v.img_src) }}">
        </div>
        {% else %}
        <div class="carousel-item">
            <img class="d-block w-100" src="{{ url_for('static', filename=v.img_src) }}">
        </div>
        {% endif %}
        {% endfor %}
    </div>
    <ul class="m-0">
        <li class="carousel-control-prev" data-bs-target="#myCarousel" data-bs-slide="prev">
            <span class="carousel-control-prev-icon" aria-hidden="true"></span>
        </li>
        <li class="carousel-control-next" data-bs-target="#myCarousel" data-bs-slide="next">
            <span class="carousel-control-next-icon" aria-hidden="true"></span>
        </li>
    </ul>
</section>

<!-- 长征•苦难辉煌 -->
<section class="cz-profile mt-1 bg-cz">
    <div class="container  py-4">
        <div class="row">
            <div class="block-title-w">
                <h2 class="block-title">长征 • 苦难辉煌</h2>
                <span class="icon-title">
                <span></span>
                <img src="{{ url_for('static', filename='images/star.png') }}" width="30" height="30">
                </span>
                <a href="{{ url_for('profile.profile_page') }}">
                <span class="sub-title mt-3">
                    {% for line in info_list %}
                        {{ line }}
                        {% if loop.index != 1 %}
                        <br>
                        {% endif %}
                    {% endfor %}
                </span>
                </a>
            </div>
        </div>
    </div>
</section>

<!-- 长征·人员损失 -->
<section class="cz-loss mt-1 py-4">
    <div class="container">
        <div class="block-title-w">
            <h2 class="block-title">长征 ·人员损失</h2>
            <span class="icon-title">
                <span></span>
                <img src="{{ url_for('static', filename='images/star.png') }}" width="30" height="30">
                </span>
            <span class="sub-title mt-3 text-primary">
                长达两年的万里征途中,红军队伍付出了极大牺牲,人员由30余万锐减至3万余人。<br>
                参考网址:https://wenku.baidu.com/view/a1ce44abb0717fd5360cdc3e.html?rec_flag=default
            </span>
        </div>
        <div class="row">
            {%for loss in cz_loss %}
            <div class="col-md-3 col-sm-6 col-xs-12 text-center">
                <div class="cz-loss-doughnut" id="{{loss.id}}"></div>
                <h2 class="cz-loss-title">{{loss.army}}</h2>
                <div class="cz-loss-decs">
                    <p>{{loss.description}}</p>
                </div>
            </div>
            {% endfor %}
        </div>
    </div>
</section>

<!--长征·人员队伍-->
<section class="cz-army mt-1 bg-army">
    <div class="container py-4">
        <div class="row">
            <div class="block-title-w">
                <h2 class="block-title">长征·红军队伍</h2>
                <span class="icon-title">
                    <span></span>
                    <img src="{{ url_for('static', filename='images/star.png') }}" width="30" height="30">
                </span>
            </div>
            {% for army in cz_army %}
            <div class="col-lg-3 col-md-3 col-sm-6 col-xs-12 p-1">
                <div class="ps-relative">
                    <a href="{{ army.href_url }}" class="image">
                        <img class="army-img" src="{{ url_for('static', filename=army.img_path) }}" alt="{{ army.name }}"/>
                    </a>
                    <div class="info">
                        <a href="{{ army.href_url }}">{{ army.name }}</a>
                    </div>
                </div>
            </div>
            {% endfor %}
        </div>
    </div>
</section>

<!--长征·红军将领-->
<section class="cz-leader mt-1 bg-cz">
    <div class="container py-4">
        <div class="block-title-w">
            <h2 class="block-title">长征·红军将领</h2>
            <span class="icon-title">
                <span></span>
                <img src="{{ url_for('static', filename='images/star.png') }}" width="30" height="30">
            </span>
        </div>
        <div class="row row-wrap">
            {% for leader in cz_leader %}
            {% if loop.index0 % 2 == 0 %}
            <div class="col-lg-3 col-md-4 col-sm-6 col-xs-12 item">
                <div class="inner mb-3">
                    <a class="image" href="{{ leader.href_url }}">
                        <img src="{{ url_for('static', filename=leader.img_path) }}"
                             class="img-fluid w-100" alt="{{ leader.name }}">
                    </a>
                    <div class="info">
                        <div class="title">
                            <a href="{{ leader.href_url }}">{{ leader.name }}</a>
                        </div>
                        <div class="sub-title">
                            <p>{{ leader.profile }}</p>
                        </div>
                        <a href="{{ leader.href_url }}" class="read-more">查看详情...</a>
                    </div>
                </div>
            </div>
            {% else %}
            <div class="col-lg-3 col-md-4 col-sm-6 col-xs-12 item item-even">
                <div class="inner mb-3">
                    <div class="info">
                        <div class="title">
                            <a href="{{ leader.href_url }}">{{ leader.name }}</a>
                        </div>
                        <div class="sub-title">
                            <p>{{ leader.profile }}</p>
                        </div>
                        <a href="{{ leader.href_url }}" class="read-more">查看详情...</a>
                    </div>
                    <a class="image" href="{{ leader.href_url }}">
                        <img src="{{ url_for('static', filename=leader.img_path) }}"
                             class="img-fluid w-100" alt="{{ leader.name }}">
                    </a>
                </div>
            </div>
            {% endif %}
            {% endfor %}
        </div>
    </div>
</section>

<!--长征·铭记历史-->
<section class="cz-memory mt-1 py-4">
    <div class="container">
        <div class="row">
            <div class="block-title-w">
                <h2 class="block-title">长征·铭记历史</h2>
                <span class="icon-title">
                    <span></span>
                    <img src="{{ url_for('static', filename='images/star.png') }}" width="30" height="30">
                </span>
            </div>
            {% for nav in cz_navs%}
            <div class="col-md-3 col-sm-6 text-center">
                <div class="tr-icon"><i class="bi {{nav.icon}}" style="font-size:4rem;color:white;"></i></div>
                <div class="tr-text">
                    <h3>{{nav.name}}</h3>
                    <span class="tr-line"></span>
                    <p>{{ nav.description }}</p>
                    <a href="{{ nav.href_url }}" class="btn-readmore" title="{{ nav.name }}">查看详情···</a>
                </div>
            </div>
            {% endfor %}
        </div>
    </div>
</section>

<!--长征·重要阶段-->
<section class="cz-stage mt-1 py-4">
    <div class="container">
        <div class="row">
            <div class="block-title-w">
                <h2 class="block-title">长征·重要阶段</h2>
                <span class="icon-title">
                    <span></span>
                    <img src="{{ url_for('static', filename='images/star.png') }}" width="30" height="30">
                </span>
            </div>
        </div>
    </div>
    <div class="container">
        <div class="row mt-5">
            {% for stage in cz_stage %}
            <div class="col-md-4 col-sm-4 p-1 {{ stage.css }}">
                <div class="cz-stage-block">
                    <div class="stage-icon"><i class="bi bi-flag-fill" style="font-size:4rem;color:red;"></i></div>
                    <div class="cz-stage-step"><span>0{{ loop.index }}</span></div>
                    <div class="cz-stage-step-title"><a href="{{ stage.href_url }}">{{ stage.name }}</a></div>
                    <div class="cz-stage-step-text"><p>{{ stage.times }}</p><p>{{ stage.description }}</p></div>
                </div>
            </div>
        {% if loop.index == 3 %}
            </div>
        </div>
        <div class="bg-cz-xys"></div>
        <div class="container">
            <div class="row mt-5">
        {% endif %}
        {% endfor %}
        </div>
    </div>
</section>

{% endblock %}

{% block jsinclude %}
<script type="text/javascript">
    let cz_loss={{ cz_loss | tojson | safe }}
</script>
<script src="{{ url_for('static', filename='js/echarts.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/cz_loss_daughnut.js') }}"></script>
{% endblock %}




4. if语句

在Jinja2中,可以使用if语句来控制模板的输出。下面是一个简单的例子,演示了如何在Jinja2模板中使用if语句:

{% if user %}  
    <p>Hello, {{ user }}!</p>  
{% else %}  
    <p>Hello, anonymous!</p>  
{% endif %}

还可以使用elif语句来添加更多的条件分支,例如:

{% if user %}  
    <p>Hello, {{ user }}!</p>  
{% elif guest %}  
    <p>Hello, guest!</p>  
{% else %}  
    <p>Hello, anonymous!</p>  
{% endif %}

5. for语句

在Jinja2中,可以使用{% for %}和{% endfor %}来实现循环。例如:

<ul>  
{% for item in items %}  
    <li>{{ item }}</li>  
{% endfor %}  
</ul>

还可以在{% endfor %}之前使用{% else %}块来处理列表为空的情况,例如下面的示例中,如果items列表为空,则会执行{% else %}块中的代码,即模板将输出在<ul>标签内输出一个内容为:“No items found.”的<li>标签:

<ul>  
{% for item in items %}  
    <li>{{ item }}</li>  
{% else %}  
    <li>No items found.</li>  
{% endfor %}  
</ul>

在Jinja2的for循环中,提供了一些变量和函数,可以使用这些变量和函数来控制循环的行为和输出。例如,可以使用{% if loop.first %}来检查是否是第一次迭代,并执行特定的操作。常用的变量和函数如表1所示:

表1 Jinja2的for循环中常用的变量和函数

变量/函数

说明

loop.index

当前迭代的索引(从1开始)

loop.index0

当前迭代的索引(从0开始)

loop.revindex

迭代序列的长度减去当前迭代的索引(从1开始)

loop.revindex0

迭代序列的长度减去当前迭代的索引(从0开始)

loop.first

如果当前迭代是第一次迭代,则为True

loop.last

如果当前迭代是最后一次迭代,则为True

loop.length

迭代序列的长度

loop.cycle(seq)

返回一个生成器,按照给定的序列循环取值

loop.depth

当前循环的嵌套深度

loop.depth0

当前循环的嵌套深度(从0开始)

loop.previtem

前一个迭代的元素

loop.nextitem

下一个迭代的元素

loop.changed(*vals)

检查给定的值是否在当前迭代中发生变化

loop.unchanged(*values)

检查给定的值是否在当前迭代中没有发生变化

loop.parent

父级循环的上下文(如果存在)

loop.children

子级循环的上下文列表

6. 模板继承实训

在接下来的实训中,将基于模板的继承,创建一个长征概述页模板文件,用于展示与长征介绍相关的内容。具体步骤如下:

  1. 在项目根目录下的templates/index/子目录内新建一个html文件,并命名为cz_profile.html,然后编写代码,实现从cz_base.html模板中继承,并重写main块的内容,如图5所示。在图中所示的代码中,在第1行通过{% extends "/cz_base.html" %}代码声明从该模板文件继承处cz_base.html父模板。从第2行-第38行代码重写了父模板的main块,实现了与首页相同的图片轮播模块:图片轮播使用了Bootstrap框架提供的图片轮播组件,轮播中所用到的图片信息由传递给模板的v_list变量提供,第5-15行代码根据v_list中的元素数量构造对应数目的指示器,在本示例中为一个短橫条,当前轮播到的图片对应的指示器为白色高亮突出显示;第16-28行代码逐个设置轮播用到的图片,图片的路径通过url_for()函数反转生成;第29-36行代码用于设置轮播区域两侧的手动前后切换轮播图片的按钮。图5 创建cz_profile.html
    {% extends "/cz_base.html" %}  
    {% block main %}
    <!-- 长征·图片轮播 -->
    <section id="myCarousel" class="carousel slide" data-bs-ride="carousel">  
        <ul class="carousel-indicators">
          {% for v in v_list %}
          {% if loop.first %}
          <li data-bs-target="#myCarousel" data-bs-slide-to="{{loop.index0}}"
              class="active" aria-current="true" aria-label="Slide{{loop.index}}"></li>
          {% else %}
          <li data-bs-target="#myCarousel" data-bs-slide-to="{{loop.index0}}"
              aria-label="Slide{{loop.index}}"></li>
          {% endif %}
          {% endfor %}
        </ul>
        <div class="carousel-inner">
          {% for v in v_list %}
          {% if loop.first %}
          <div class="carousel-item active">
            <img class="d-block w-100" src="{{ url_for('static',filename=v.img_src) }}">
          </div>
          {% else %}
          <div class="carousel-item">
            <img class="d-block w-100" src="{{ url_for('static', filename=v.img_src) }}">
          </div>
          {% endif %}
          {% endfor %}
        </div>
        <ul class="m-0">
            <li class="carousel-control-prev" data-bs-target="#myCarousel" data-bs-slide="prev">
                <span class="carousel-control-prev-icon" aria-hidden="true"></span>
            </li>
            <li class="carousel-control-next" data-bs-target="#myCarousel" data-bs-slide="next">
                <span class="carousel-control-next-icon" aria-hidden="true"></span>
            </li>
        </ul>
    </section>  
    {% endblock %}

  2. 修改cz_profile.py (位于项目根目录下的views/子目录下), 如图6所示。首先在文件的第1行代码中,导入了Flask框架下的Blueprint类及render_template()方法;第2行代码处从global_data模块导入了v_list变量,用于为图片轮播模块提供数据;第4行代码创建了一个Blueprint对象(蓝图对象,是Flask实现应用模块化的重要组件,将在后面的实训中进行学习),第8-10行代码处定义了一个函数profile_page(),在函数中利用render_template()方法渲染(1)中定义的模板cz_profile.html。在第7行代码处通过装饰器route()将路由URL“/profile”绑定到profile_page函数。图6 在cz_profile.py模块中定义视图函数及路由
    from flask import Blueprint, render_template
    from models.global_data import v_list
    
    profile = Blueprint('profile', __name__)
    
    
    @profile.route('/profile')
    def profile_page():
        # 利用render_template方法,将相关数据渲染到模板文件中
        return render_template("index/cz_profile.html", v_list=v_list)

  3. 修改完成后,在浏览器输入长征概述页的URL地址(),或从首页的“长征•苦难辉煌”模块的文本内容区域单击进入到长征概述页页面,应该能看到新添加的页面呈现效果,如图7所示。
  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值