在“致敬长征”项目框架根目录下的/static/jsons子目录下,提供于关于本项目中将要使用的部分数据,这些数据以json文件格式存储在相应的文件中,接下来的实训将实现从cz_profile_data.json数据文件(如图1所示)中读取关于长征历史背景的介绍内容并将其在长征概述页中呈现出来。
实训的具体步骤如下:
1. 从示例文件中加载数据并传递给模板:cz_profile_data.json文件中存储的数据是JSON数据格式的,类似于Python中的字典结构,也是以key-value的键值对形式存储数据。在“长征•历史背景”模块将提取“czProfileSubTitle1”、“czProfileSubTitle2”、 “czProfileDescription”、“czProfileIntroduction”及“czProfileReferences”五个键所对应的值,并呈现在页面中。其中前三个键与对应的值在文件中是位于同一行文本中,后两个键所对应的值为一个包含多行文本的数组(以一对中括号“[]”括住,类似于Python中的列表)。因此,本实训中,修改cz_profile.py文件的视图函数profile_page(),利用文件读取及字符串操作的相关方法,实现从文本文件提取对应的数据,并将提取出来的内容存储于一个字典结构的变量profiles中,最后通过render_template()方法将数据传递到模板文件cz_profile.html中。具体实现如图2所示。
import os
from flask import Blueprint, render_template
from models.global_data import v_list
profile = Blueprint('profile', __name__)
@profile.route('/profile')
def profile_page():
fpath = os.path.join(os.getcwd(), "static/jsons/cz_profile_data.json") # 构造示例数据文件的路径字符串
keys = [ 'czProfileSubTitle1','czProfileSubTitle2','czProfileDescription',
'czProfileIntroduction','czProfileReferences'] # 待提取内容的查找关键字
idx = 0
profiles = {} # 用于保存读取到的相关内容
# 用open方法,根据示例数据文件路径打开json文件,并用json模块的Load方法将数据加载到相应变量中
with open(fpath, mode='r', encoding='utf-8') as fp:
data = fp.readline() # 读入第一行内容
key = keys[idx] # 取出第一个待提取内容对应的关键字
while data: # 逐行处理读取的文本行
if data.find(key) > -1: # 当前读入的文本行中包含查找关键字,说明本行的文本是要提取的内容
if idx < 3: # 如果是keys中的前三个关键字,则待提取内容全部在当前行中最后两个英文的双引号之间
end = data.rfind('"') # 找最后一个英文的双引号出现的索引值
start = data.rfind('"', 0, end - 1) # 找倒数第二个英文的双引号出现的索引值
profiles[key] = data[start + 1:end] # 利用字符串切片的方式提取对应文本,并保存到字典profiles中
else: # 如果是keys中的后二个关键字,则待提取内容全部在当前行的下一行至接下来遇到的第一个右中括号(])所在行的上一行
profiles[key] = [] # 后两个关键字对应的内容有多行,因此用一个列表来存储
temp = fp.readline()
while temp.find(']') == -1: # 逐行读取并处理,直至遇到结束行的标志(一个右中括号(])所在的行)
start = temp.find('"') # 找第一个英文的双引号出现的索引值
end = temp.rfind('"') # 找最后一个英文的双引号出现的索引值
profiles[key].append(temp[start + 1:end]) # 利用字符串切片的方式提取对应文本,并添加到对应的列表中
temp = fp.readline() # 读入下一行内容,以便循环处理
idx += 1 # 索引值加1,以便读取查找关键字列表中下一个关键字
if idx < len(keys): # 判断索引值是否超出了keys的长度,以防止取查找关键字时出现索引越界的错误
key = keys[idx] # 取出下一个待提取内容对应的关键字
data = fp.readline() # 读入下一行内容,以便循环处理
# 利用render_template方法,将相关数据渲染到模板文件中
return render_template("index/cz_profile.html", v_list=v_list, profiles=profiles)
2. 编写模板文件中对应的“长征•历史背景”模块的代码:在cz_profile.html模板中,通过模板语法将profiles变量中的文本内容渲染到页面中,具体如图3所示。其中“czProfileSubTitle1”、“czProfileSubTitle2”和“czProfileDescription”三个键对应的内容是一个字符串,因此直接使用变量取值语法“{{…}}”将对应的文本内容填充到网页的标签内;“czProfileIntroduction”及“czProfileReferences”两个键对应的内容都是一个列表,因此使用了循环结构{% for … %}…{% endfor %}将列表中的每个元素渲染到页面中。同时使用相关的Bootstrap类样式或自定义的类样式控制文本在页面中的呈现效果。须注意的是,因cz_profile.html是从cz_base.html模板中继承的,页面的相关内容均位于重写了父模板的main模块中,因此“长征•历史背景”模块的全部代码(位于一对<section></section>标签内),应该位于“长征•图片轮播”模块的结束标签</section>后面,main块的结束标志{% endblock %}前面的代码区间内。
{% 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>
<!-- 长征·历史背景 -->
<section class="mt-1">
<div class="container pt-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>
<img src="{{ url_for('static', filename='images/cz_bar.jpg') }}" class="rounded w-100 mt-3">
<div class="text-start">
<span class="text-danger fw-bold fs-4 mt-3">
<img src="{{ url_for('static', filename='images/cz.jpg') }}" class="rounded" height="26">
{{ profiles.czProfileSubTitle1 }}</span>
<span class="sub-title fw-bold fs-4 mt-3 text-primary">
{{ profiles.czProfileSubTitle2 }}</span>
<span class="text-warning sub-title fw-semibold fs-4 mt-3">
{{ profiles.czProfileDescription }}</span>
{% for intro in profiles.czProfileIntroduction %}
<span class="sub-title {{ loop.cycle('bg-warning', 'bg-light') }} fw-medium fs-5 mt-3 p-3 rounded-4">
{{ intro }}</span>
{% endfor %}
<h2 class="block-titLe">参考资料</h2>
{% for ref in profiles.czProfileReferences %}
<span class="sub-title bg-light fw-medium fs-5 p-3 rounded-4 mb-1">{{ ref }}</span>
{% endfor %}
</div>
</div>
</div>
</div>
</section>
{% endblock %}
3. 完成以上工作后,在浏览器访问:http://127.0.0.1:19999/profile,或从首页中单击“长征•苦难辉煌”模块中的文字进入到长征概述页,查看“长征•历史背景”模块的呈现效果,如图4所示。