Python全栈(七)Flask框架之4.Flask模板继承与案例练习

Jewel ·
更新时间:2024-11-13
· 800 次阅读

Flask模板继承与案例练习一、模版继承二、配置静态资源文件三、模板案例 一、模版继承

Flask中的模板可以继承,把模板中重复出现的元素抽取出来放在父模板中,子模板再根据自己的需要进行改写。
通常,在父模板中定义公用的部分,通过定义block给子模板开一个口,子模板从父模板中继承并做改动,从而提高了代码的复用性

采用原始的简单复制的方法测试:
创建视图函数Python文件:

from flask import Flask, render_template app = Flask(__name__) @app.route('/') def index(): return render_template('index.html') @app.route('/list/') def course_list(): return render_template('list.html') if __name__ == '__main__': app.run(debug=True)

创建templates目录,在其下创建index.html和list.html,index.html如下:

首页

这是首页

  • 课程列表
  • 课程详情
  • 视频教程
  • 关于我们

list.html如下:

课程列表

这是课程列表页面

  • Python
  • Java
  • PHP
  • C++

显示:
flask template inheritance origin
显然,两个网页结构相同,并且在模板代码中元素基本相同,有大量的代码重复,可以进行优化。

现使用模板继承进行测试:

from flask import Flask, render_template app = Flask(__name__) app.config['TEMPLATES_AUTO_RELOAD'] = True @app.route('/') def index(): return render_template('index.html') @app.route('/list/') def course_list(): return render_template('list.html') if __name__ == '__main__': app.run(debug=True)

新建base.html即父模板:

{% block title %} {% endblock %} {% block header %}

这是首页

{% endblock %} {% block content %}
  • 课程列表
  • 课程详情
  • 视频教程
  • 关于我们
{% endblock %} {% block footer %} {% endblock %}

base.html抽取了所有模板都需要用到的元素html、body等公共部分,并且对于所有模板都要用到的样式文件也进行了抽取,同时对于子模板需要重写的地方,比如title、head、body都定义成了block,子模板继承自base.html后可以根据自己的需要,再具体实现。
让index.html和list.html继承自base.html,index.html如下:

{% extends 'base.html' %} {% block title %} 这是首页 {% endblock %} {# 重写父模板中的block #} {% block header %}

这是子模版首页

{% endblock %}

list.html如下:

{% extends 'base.html' %} {% block title %} 这是首页 {% endblock %} {# 重写父模板中的block #} {% block header %}

这是子模版课程列表页面

{% endblock %} {% block content %}
  • Python
  • Java
  • PHP
  • C++
{% endblock %}

显示:
flask template inheritance base
显然,在使用模板继承后,代码的复用性明显提高、代码量显著减少;
{% extends 'base.html' %}定义了该模板继承的父模板,然后在相应的block中重写所需内容;
在父模板中放入block中的代码子模板继承后可以重写,而父模板中未放入block中的代码子模板只能继承不能重写;
模板中的block不能重名。

在父模板和子模板中block还可以嵌套,测试如下:
base.html:

{% block title %} {% endblock %} {% block header %}

这是首页

{% endblock %} {% block content %}
  • 课程列表
  • 课程详情
  • 视频教程
  • 关于我们
{% endblock %} {% block footer %} {% block footer-link %} {% endblock %} {% endblock %}

index.html:

{% extends 'base.html' %} {% block title %} 这是首页 {% endblock %} {# 重写父模板中的block #} {% block header %}

这是子模版首页

{% endblock %} {% block footer %} {% block footerlink %} {% endblock %} {% endblock %}

list.html:

{% extends 'base.html' %} {% block title %} 这是首页 {% endblock %} {# 重写父模板中的block #} {% block header %}

这是子模版课程列表页面

{% endblock %} {% block content %}
  • Python
  • Java
  • PHP
  • C++
{% endblock %} {% block footerlink %} {% endblock %}

显示:
flask template inheritance block nesting
显然,在父模板中block定义嵌套后,在子模板中重写block时,可以将两层嵌套都引用再重写,如index.html,也可以只引用内部block嵌套并重写,可根据需要选择。
注意:
父模板中的block相当于给子模板提供了一个接口,只有父模板中已经定义了,才能在父模板中实现;
子模板中不能扩展代码,即自己在block之外添加的代码是无效的,子模版中的所有代码都必须在父模板及其定义的block中实现,测试如下:
index.html中添加代码:

{% extends 'base.html' %} {% block title %} 这是首页 {% endblock %} {% block header %}

这是子模版首页

{% endblock %} {% block footer %} {% block footerlink %} {% endblock %} {% endblock %}

这是在block之外的代码

显示:
flask template inheritance outside block
显然,在block之外的代码并没有被渲染出来。

同时,一个子模板只能继承自一个父模板,不能多继承。
如果在一个地方需要用到另一个block的内容,需要使用self.block名字来引用,测试(index.html代码)如下:

{% extends 'base.html' %} {% block title %} 这是首页 {% endblock %} {% block header %}

这是子模版首页

{% endblock %} {% block footer %}

这是本网页标题---{{ self.title() }}---

{% block footerlink %} {% endblock %} {% endblock %}

显示:
flask template inheritance block self
显然,在footer中引用了title中的内容。

调用父模板中的block时,在改写父模板的同时还想得到父模板中的代码时,可以类比Python中类的继承,使用super()函数即调用了父模板中的代码,把父模板中的内容添加到子模板中,测试如下:
index.html代码修改如下:

{% extends 'base.html' %} {% block title %} 这是首页 {% endblock %} {% block header %} {{ super() }}

这是子模版首页

{% endblock %} {% block footer %} {% block footerlink %} {% endblock %} {% endblock %}

显示:
flask template inheritance super
显然,是可以继承同时重写父模板的。

二、配置静态资源文件

静态文件包括包括CSS样式文件、JavaScript脚本文件、图片文件、字体文件等;
实际的前端开发中会使用大量的静态文件来使得网页更加生动美观,在Jinja2中加载静态文件通过url_for()函数来实现,例如

url_for()函数默认会在项目根目录下的static文件夹的css目录下中寻找test.css文件,如果文件存在,则会生成一个相对于项目根目录的/static/about.css路径。
如果不想放在static目录、自己指定目录,可以在初始化Flask时,设置static_folder参数,例如app = Flask(__name__,static_folder='static_files'),此时会到static_files目录下寻找静态文件。

加载静态文件测试:
在模板文件夹下创建books.html如下:

图书列表

这是图书列表页面

蒙娜丽莎的微笑
  • Python
  • Java
  • PHP
  • C++

在项目目录下创建static目录,下面创建css、js、images三个文件夹、分别用于保存CSS、JavaScript和图片文件,用于保存资源文件,css目录下创建books.css如下:

body { background: #4eb8ff; }

js目录下创建books.js如下:

alert(document.domain);

找一张图片如monalisa.jpg放在images目录下,进行测试:

flask模板静态文件加载

显然,此时已加载到css、js、images静态文件。
考虑模板继承,css和JavaScript一般在父模板中直接在头部加入,而图片在子模板的对应位置的block中加入,如下:
base.html:

{% block title %} {% endblock %} {% block header %}

这是首页

{% endblock %} {% block content %}
  • 课程列表
  • 课程详情
  • 视频教程
  • 关于我们
{% endblock %} {% block footer %} {% block footerlink %} {% endblock %} {% endblock %}

index.html:

{% extends 'base.html' %} {% block title %} 这是首页 {% endblock %} {% block header %}

这是子模版首页

蒙娜丽莎的微笑 {% endblock %} {% block footer %} {% block footerlink %} {% endblock %} {% endblock %}

list.html:

{% extends 'base.html' %} {% block title %} 这是首页 {% endblock %} {# 重写父模板中的block #} {% block header %}

这是子模版课程列表页面

{% endblock %} {% block content %}
  • Python
  • Java
  • PHP
  • C++
{% endblock %} {% block footerlink %} 蒙娜丽莎的微笑 {% endblock %}

显示:
flask template static resource inheritance
显然,在父模板中定义的CSS样式和JavaScript在子模板中全部继承并渲染。

三、模板案例

目标是做一个豆瓣电影的电影、电视剧评分页面图,预期效果如下:
豆瓣预期效果
本项目用到的素材可点击https://download.csdn.net/download/CUFEECR/12327362下载测试。

先初步测试:
建立视图函数Python文件:

from flask import Flask, render_template app = Flask(__name__) app.config['TEMPLATES_AUTO_RELOAD'] = True @app.route('/') def index(): return render_template('index.html') if __name__ == '__main__': app.run(debug=True)

再创建templates文件夹并在其下创建index.html:

豆瓣

豆瓣影视评分

电影 更多

绅士们

{% set rating = 8.4 %} {% set rating_light = ((rating|int)/2)|int %} {% set rating_half = (rating|int)%2 %} {% set rating_gray = 5 - rating_light - rating_half %} {% for foo in range(0, rating_light) %} {% endfor %} {% for foo in range(0, rating_half) %} {% endfor %} {% for foo in range(0, rating_gray) %} {% endfor %} {{ rating }}

饥饿站台

{% set rating = 7.8 %} {% set rating_light = ((rating|int)/2)|int %} {% set rating_half = (rating|int)%2 %} {% set rating_gray = 5 - rating_light - rating_half %} {% for foo in range(0, rating_light) %} {% endfor %} {% for foo in range(0, rating_half) %} {% endfor %} {% for foo in range(0, rating_gray) %} {% endfor %} {{ rating }}

消失吧,群青

{% set rating = 6.7 %} {% set rating_light = ((rating|int)/2)|int %} {% set rating_half = (rating|int)%2 %} {% set rating_gray = 5 - rating_light - rating_half %} {% for foo in range(0, rating_light) %} {% endfor %} {% for foo in range(0, rating_half) %} {% endfor %} {% for foo in range(0, rating_gray) %} {% endfor %} {{ rating }}

创建static目录,在其下创建css目录和images目录,再在css目录下创建base.css和item.css,base.css如下:

*{ margin: 0; padding: 0; list-style: none; text-decoration: none; } .container{ width: 375px; height: 600px; background: pink; } .search-group{ padding: 14px 8px; background: #41be57; } .search-group .search-input{ background: #fff; display: block; width: 100%; height: 30px; border-radius: 5px; outline: none; border: none; }

item.css如下:

.item-list-group .item-list-top{ overflow: hidden; padding: 10px; } .item-list-top .module-title{ float: left; } .item-list-top .more-btn{ float: right; } .list-group{ overflow: hidden; padding: 0 10px; } .item-group{ float: left; margin-right: 10px; } .item-group .thumbnail{ display: block; width: 100px; height: 150px; } .item-group .item-title{ font-size: 12px; text-align: center; } .item-group .item-rating{ font-size: 12px; text-align: center; } .item-rating img{ width: 10px; height: 10px; }

在images目录下放入所需图片文件,测试如下:
flask template exercise origin
显然,达到了预期效果,但是在index.hml代码中重复代码很多,复用性较低,可以进行优化。

现使用宏将显示一部电影信息的HTML代码抽象出来,再传入所需参数,如下:

豆瓣 {% macro itemGroup(thumbnail, title, rating) %}

{{ title }}

{% set rating_light = ((rating|int)/2)|int %} {% set rating_half = (rating|int)%2 %} {% set rating_gray = 5 - rating_light - rating_half %} {% for foo in range(0, rating_light) %} {% endfor %} {% for foo in range(0, rating_half) %} {% endfor %} {% for foo in range(0, rating_gray) %} {% endfor %} {{ rating }}

{% endmacro %}

豆瓣影视评分

电影 更多
{{ itemGroup('static/images/绅士们.jpg', '绅士们', 8.4) }} {{ itemGroup('static/images/饥饿站台.jpg', '饥饿站台', 7.8) }} {{ itemGroup('static/images/消失吧,群青.jpg', '消失吧,群青', 6.7) }}

测试效果与之前相同,此时显然代码缩短了很多,提高了代码的复用性。

但是上面是直接将数据写入HTML中的,真实环境中一般是通过请求数据库获得的,现在模板函数代码中模拟数据的查询与渲染:

from flask import Flask, render_template app = Flask(__name__) app.config['TEMPLATES_AUTO_RELOAD'] = True #  电影 movies = [ { 'title': u'绅士们', 'thumbnail': 'static/images/绅士们.jpg', 'rating': u'8.4', }, { 'title': u'饥饿站台', 'thumbnail': u'static/images/饥饿站台.jpg', 'rating': u'7.8', }, { 'title': u'消失吧,群青', 'thumbnail': u'static/images/消失吧,群青.jpg', 'rating': u'6.7', }, { 'title': u'再见,妈妈', 'thumbnail': u'static/images/再见,妈妈.jpg', 'rating': u'8.1', }, { 'title': u'狩猎', 'thumbnail': u'static/images/狩猎.jpg', 'rating': u'7.3', }, { 'title': u'隐形人', 'thumbnail': u'static/images/隐形人.jpg', 'rating': u'7.3', } ] # 电视剧 tvs = [ { 'title': u'清平乐', 'thumbnail': 'static/images/清平乐.jpg', 'rating': u'8.1', }, { 'title': u'民国神探', 'thumbnail': u'static/images/民国神探.jpg', 'rating': u'7.1', }, { 'title': u'叹息桥', 'thumbnail': u'static/images/叹息桥.jpg', 'rating': u'8.8', }, { 'title': u'重生', 'thumbnail': u'static/images/重生.jpg', 'rating': u'6.6', }, { 'title': u'陈情令', 'thumbnail': u'static/images/陈情令.jpg', 'rating': u'7.7', }, { 'title': u'锦衣之下', 'thumbnail': u'static/images/锦衣之下.jpg', 'rating': u'7.6', } ] @app.route('/') def index(): context = { 'movies': movies, 'tvs': tvs } return render_template('index.html', **context) if __name__ == '__main__': app.run(debug=True)

模板index.html代码:

豆瓣 {% macro itemGroup(thumbnail, title, rating) %}

{{ title }}

{% set rating_light = ((rating|int)/2)|int %} {% set rating_half = (rating|int)%2 %} {% set rating_gray = 5 - rating_light - rating_half %} {% for foo in range(0, rating_light) %} {% endfor %} {% for foo in range(0, rating_half) %} {% endfor %} {% for foo in range(0, rating_gray) %} {% endfor %} {{ rating }}

{% endmacro %}

豆瓣影视评分

电影 更多
{% for movie in movies[:3] %} {{ itemGroup(movie.thumbnail, movie.title, movie.rating) }} {% endfor %}

电视剧 更多
{% for tv in tvs[:3] %} {{ itemGroup(tv.thumbnail, tv.title, tv.rating) }} {% endfor %}

显示:
flask template exercise macro
显然,已经达到了初步效果。

可以看到,在展示电影和电视剧的模板代码部分,还是有部分重复的代码,因此可以再定义一个宏,如下:

豆瓣 {% macro itemGroup(thumbnail, title, rating) %}

{{ title }}

{% set rating_light = ((rating|int)/2)|int %} {% set rating_half = (rating|int)%2 %} {% set rating_gray = 5 - rating_light - rating_half %} {% for foo in range(0, rating_light) %} {% endfor %} {% for foo in range(0, rating_half) %} {% endfor %} {% for foo in range(0, rating_gray) %} {% endfor %} {{ rating }}

{% endmacro %} {% macro listGroup(type, items) %}
{{ type }} 更多
{% for item in items[:3] %} {{ itemGroup(item.thumbnail, item.title, item.rating) }} {% endfor %}
{% endmacro %}

豆瓣影视评分

{{ listGroup('电影', movies) }}
{{ listGroup('电视剧', tvs) }}

效果与前者相同。

现进行进一步扩展,将公共部分代码抽象到父模板中,重复代码定义到宏中,进而来实现电影和电视剧详情列表。
视图函数代码如下:

from flask import Flask, render_template, request app = Flask(__name__) app.config['TEMPLATES_AUTO_RELOAD'] = True #  电影 movies = [ { 'title': u'绅士们', 'thumbnail': '../static/images/绅士们.jpg', 'rating': u'8.4', }, { 'title': u'饥饿站台', 'thumbnail': u'../static/images/饥饿站台.jpg', 'rating': u'7.8', }, { 'title': u'消失吧,群青', 'thumbnail': u'../static/images/消失吧,群青.jpg', 'rating': u'6.7', }, { 'title': u'再见,妈妈', 'thumbnail': u'../static/images/再见,妈妈.jpg', 'rating': u'8.1', }, { 'title': u'狩猎', 'thumbnail': u'../static/images/狩猎.jpg', 'rating': u'7.3', }, { 'title': u'隐形人', 'thumbnail': u'../static/images/隐形人.jpg', 'rating': u'7.3', } ] # 电视剧 tvs = [ { 'title': u'清平乐', 'thumbnail': '../static/images/清平乐.jpg', 'rating': u'8.1', }, { 'title': u'民国神探', 'thumbnail': u'../static/images/民国神探.jpg', 'rating': u'7.1', }, { 'title': u'叹息桥', 'thumbnail': u'../static/images/叹息桥.jpg', 'rating': u'8.8', }, { 'title': u'重生', 'thumbnail': u'../static/images/重生.jpg', 'rating': u'6.6', }, { 'title': u'陈情令', 'thumbnail': u'../static/images/陈情令.jpg', 'rating': u'7.7', }, { 'title': u'锦衣之下', 'thumbnail': u'../static/images/锦衣之下.jpg', 'rating': u'7.6', } ] @app.route('/') def index(): context = { 'movies': movies, 'tvs': tvs } return render_template('index.html', **context) @app.route('/list/') def show_list(): category = int(request.args.get('category')) if category == 1: items = movies mtype = '电影' else: items = tvs mtype = '电视剧' return render_template('list.html', items = items, mtype = mtype) if __name__ == '__main__': app.run(debug=True)

创建base.html如下:

{% from 'macros.html' import itemGroup, listGroup %} {% block title %} {% endblock %} {% block body_title %}

豆瓣影视评分

{% endblock %}
{% block content %} {% endblock %}

创建macro.html保存宏:

{% macro itemGroup(thumbnail, title, rating) %}

{{ title }}

{% set rating_light = ((rating|int)/2)|int %} {% set rating_half = (rating|int)%2 %} {% set rating_gray = 5 - rating_light - rating_half %} {% for foo in range(0, rating_light) %} {% endfor %} {% for foo in range(0, rating_half) %} {% endfor %} {% for foo in range(0, rating_gray) %} {% endfor %} {{ rating }}

{% endmacro %} {% macro listGroup(type, items, category=category) %}
{{ type }} 更多
{% for item in items[:3] %} {{ itemGroup(item.thumbnail, item.title, item.rating) }} {% endfor %}
{% endmacro %}

index.html代码为:

{% extends 'base.html' %} {% block title %} 首页 {% endblock %} {% block body_title %}

豆瓣影视评分

{% endblock %} {% block content %} {{ listGroup('电影', movies, 1) }}
{{ listGroup('电视剧', tvs, 2) }} {% endblock %}

创建list.html代码为:

{% extends 'base.html' %} {% block title %} {{ mtype }} {% endblock %} {% block body_title %}

豆瓣{{ mtype }}评分

{% endblock %} {% block content %} {% for item in items %} {{ itemGroup(item.thumbnail, item.title, item.rating) }} {% endfor %} {% endblock %}

显示:
flask template exercise final
显然,已经达到了基本的预期效果。

在实现的过程中有关键的几点需要注意:

在定义宏时要注意代码重复的范围,并设置恰当的参数; 在设计模板继承时,要考虑到各个页面公共的代码,同时要注意在父模板中引用宏,在子模版中直接继承父模板,而不需要再引用宏; 在定义宏时,在listGroup()部分需要传入参数来判断在主页点击的是电影还是电视剧,从而返回相应的数据,这主要是使用url_for()函数进行映射,因为在show_list()函数中不存在category参数,所以在请求时会将参数体现到URL中,再在视图函数中通过request.args.get()方法来得到选择的category来传递数据。
作者:cutercorley



继承 flask Python python全栈

需要 登录 后方可回复, 如果你还没有账号请 注册新账号