flask 模板继承(extends)

前面教程着重于讲解如何在flask模板里渲染后端传过来的数据,模板继承解决的是如何用更少的代码组织前端页面的问题。如果你有前端编程的经验,那么有一件事情一定会让你感到烦恼,前端页面里的很多代码都是重复使用的,每次编写一个新的页面,都不得不将之前页面里的代码复制粘贴过来。

模板继承,同类的继承一样,都很好的解决了代码复用问题,同时为前端页面代码提供了更清晰的组织结构。

1. 代码复用

我的网站打算使用bootstrap进行布局,因此每个页面都需要引入bootstrap的css和js文件,但是我不想这么做,我希望下面的代码只写一次就足够了

<link href="/static/public/css/bootstrap.min.css" rel="stylesheet">
<script src="/static/public/js/jquery-3.0.0.min.js"></script>
<script src="/static/public/js/bootstrap.bundle.min.js"></script>

这是我的目标,那么如何用flask模板继承技术实现呢

1.1 编写基类模板

创建base.html文件,内容如下

<!DOCTYPE html>
<html lang="en">

<head>
    {% block head %}
    <meta charset="UTF-8">
    <title>{% block title %}{% endblock %}' </title>
    <link href="/static/public/css/bootstrap.min.css" rel="stylesheet">
    <script src="/static/public/js/jquery-3.0.0.min.js"></script>
    <script src="/static/public/js/bootstrap.bundle.min.js"></script>
    {% endblock %}
</head>

<body>
    {% block body %}
    {% endblock %}
</body>
</html>
    

{% block %} 定义了子模板可以填充的块,分别是head,title,body,这就是网站页面布局的基本结构。

1.2 编写子模板

子模板可以继承基类模板,并填充在基类模板里定义的block块。

index.html

{% extends 'base.html' %}

{% block title %}这是首页{% endblock %}

{% block body %}
    <p>这是index</p>
{% endblock %}
    

在index.html里,你没有看到任何html相关的代码,但它却可以被flask渲染成一个完整的页面,秘密在于这句话

{% extends 'base.html' %}

index.html继承了base.html,base.html里的html源码虽然没有出现在index.html模板里,但在渲染时会视其真实存在。所以,bootstrap资源的引入代码就无需在index.html模板里编写了。

在实现代码复用的同时,也要考虑每个页面里差异化的部分,比如title,页面的标题总不能都一样吧,因此在定义base模板时,预留了title 块,字模板在继承base模板后,可以填充title块

{% block title %}这是首页{% endblock %}

前端在渲染时,每个页面都会有自己的title标签,以下是app.py 的内容,你不妨实际运行程序来观察继承后的index.html页面

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/index')
def index():
    return render_template("index.html")

if __name__ == '__main__':
    app.run(debug=True)

2. super 块

super块可以更灵活的应对子模板的差异化要求,考虑一下这种情况,每个页面都会引入bootstrap的资源,但个别页面还会引入其他的js脚本,只是为了这个页面里的某个特殊功能

<script src="/static/mk/js/mk.js"></script>

mk.js并非每个页面都会使用,因此不适合放在base.html里,在需要它的页面里该如何引入呢?可以使用super 先渲染父级块内容,然后再编写差异化的部分,示例index2.html



{% extends 'base.html' %}

{% block title %}这是index2页面{% endblock %}

{% block head %}
    {{ super() }}
    <script src="/static/mk/js/mk.js"></script>
{% endblock %}


{% block body %}
    <p>这是index2页面</p>
{% endblock %}
    

现将base模板里的head块进行渲染,head块里也包含了mk.js的引入代码。

我个人并不喜欢用这种方式组织代码,如果要处理子模板的差异化,我更喜欢提前在base预留block,下面是我的解决方式

base.html

<head>
    {% block head %}
    <meta charset="UTF-8">
    <title>{% block title %}{% endblock %}</title>
    <link href="/static/public/css/bootstrap.min.css" rel="stylesheet">
    <script src="/static/public/js/jquery-3.0.0.min.js"></script>
    <script src="/static/public/js/bootstrap.bundle.min.js"></script>
        {% block js_css %}
        {% endblock %}
    {% endblock %}
</head>
    

index2.html

{% extends 'base.html' %}

{% block title %}这是index2页面{% endblock %}

{% block js_css %}
    <script src="/static/mk/js/mk.js"></script>
{% endblock %}


{% block body %}
    <p>这是index2页面</p>
{% endblock %}
    

这种方式的好处是组织结构更加清晰明了,代码编写更加舒畅。

3. include

include 技术与模板技术并无关联,但它们两个一起配合才能完整对前面代码的组织与管理。include做的事情是将一部分独立性比较强的代码提取到单独文件中进行管理。

编写top_navbar.html文件

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <div class="navbar-brand">
            <!--<a class="navbar-brand" href="#">菜鸟教程</a>-->
        <img src="/static/public/pic/coolpython_pic.png" width="100" height="100%" alt="酷python" />
    </div>
    <div class="collapse navbar-collapse">
        <ul class="navbar-nav mr-auto">
            <li class="nav-item dropdown">
                <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#">python教程<b class="caret bottom-up"></b></a>
                <div class="dropdown-menu">
                    <a class="dropdown-item" href="/python_primary/python_primary_tutorial.html">python基础教程</a>
                    <a class="dropdown-item" href="/python_senior/index.html">python进阶教程</a>
                </div>
            </li>
        </ul>
    </div>
</nav>

修改base.html

<body>

    {% include 'top_navbar.html' %}
    {% block body %}
    {% endblock %}
    
</body>

每个字模板都继承了base模板,每个页面的最上方都是菜单栏,变动菜单栏时只需修改top_navbar.html即可。

4. 命名块结束标签

前面的示例中,每个block都是用endblock结束的,当block出现嵌套时,难免不让人感到混乱,无法确定endblock结束的是哪一个block。针对这种情形,你可以在endblock时指定block的名称

<head>
    {% block head %}
    <meta charset="UTF-8">
    <title>{% block title %}{% endblock title %}</title>
    <link href="/static/public/css/bootstrap.min.css" rel="stylesheet">
    <script src="/static/public/js/jquery-3.0.0.min.js"></script>
    <script src="/static/public/js/bootstrap.bundle.min.js"></script>
        {% block js_css %}
        {% endblock js_css %}
    {% endblock head %}
</head>
    

指定block名称,就可以轻松识别endblock结束的是哪个block。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案