购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

4.4 模板结构

Jinja2比传统HTML拥有更加强大的功能,其中一个就表现在代码模块化上。HTML除了通过iframe标签在浏览器端动态加载其他网页,几乎不具备任何代码模块化的能力。而Jinja2则可以通过宏、模板继承、引入模板等方式实现代码模块化,下面分别进行讲解。

4.4.1 宏和import语句

模板中的宏与Python中的函数类似,可以传递参数,但是不能有返回值。可以将一些常用的代码片段放到宏中,然后把一些不固定的值抽取出来当成一个参数,下面用一个例子来阐述宏的用法。

    {% macro input(name,value=", type='text') %}
       <input type="{{ type }}" value="{{ value|escape }}" name="{{ name }}">
    {% endmacro %}

以上代码通过macro标签创建了一个叫作input的宏,这个宏接收两个参数,分别是name和type。以后在创建input标签时,可以通过以下代码快速创建。

    <div>{{ input('username') }}</div>
    <div>{{ input('password', type='password') }}</div>

在实际的开发工作中,经常会将一些常用的宏单独放到一个文件中,在需要使用时再从这个文件进行导入。导入使用的是import语句,import语句的用法与Python中的import类似,形式可以直接是import…as…,也可以是from…import…,或者是from…import…as…。下面先创建一个forms.html,然后添加以下代码。

    {% macro input(name, value='', type='text') %}
       <input type="{{ type }}" value="{{ value|escape }}" name="{{ name }}">
    {% endmacro %}
     
    {% macro textarea(name, value='', rows=10, cols=40) %}
       <textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols
       }}">{{ value|escape }}</textarea>
    {% endmacro %}

在forms.html中添加了两个宏,分别是input和textarea。下面再在另外一个文件中通过import语句进行导入。

(1)通过import…as…形式导入。

    {% import 'forms.html' as forms %}
    <dl>
     <dt>Username</dt>
     <dd>{{ forms.input('username') }}</dd>
     <dt>Password</dt>
     <dd>{{ forms.input('password', type='password') }}</dd>
    </dl>
    <p>{{ forms.textarea('comment') }}</p>

(2)通过from…import…as…或者from…import…形式导入。

    {% from 'forms.html' import input as input_field, textarea %}
    <dl>
      <dt>Username</dt>
      <dd>{{ input_field('username') }}</dd>
      <dt>Password</dt>
      <dd>{{ input_field('password', type='password') }}</dd>
    </dl>
    <p>{{ textarea('comment') }}</p>

需要注意的是,通过import导入模板并不会把当前模板的变量添加到被导入的模板中,如果要在被导入的模板中使用当前模板的变量,可以通过以下两种方式实现。

(1)显示地通过参数形式传递变量。

(2)使用with context的方式,示例代码如下。

    {% from 'forms.html' import input with context %}

通过以上两种方式,在forms.html中的代码也可以使用当前模板的所有变量了。

4.4.2 模板继承

一个网站中的大部分网页的模块是重复的,如顶部的导航栏、底部的备案信息。如果在每个页面中都重复地去写这些代码,会让项目变得臃肿,增加后期的维护成本。比较好的做法是,通过模板继承,把一些重复性的代码写在父模板中,子模板继承父模板后,再分别实现自己页面的代码。首先来看一个父模板base.html的例子。

    <!DOCTYPE html>
    <html lang="en">
    <head>
       <link rel="stylesheet" href="base.css" />
       <title>{% block title %}{% endblock %}</title>
       {% block head %}{% endblock %}
    </head>
    <body>
       <div id="body">{% block body %}{% endblock %}</div>
       <div id="footer">
           {% block footer %}
           &copy; Copyright 2008 by <a href="http://domain.invalid/">you</a>
           {% endblock %}
       </div>
    </body>
    </html>

在以上父模板中编写了网页的整体结构,并且把所有子模板都需要用到的样式文件base.css也提前做了引用。针对子模板需要重写的地方,则定义成了block,如title、head、body、footer,子模板在继承了父模板后,重写对应block的代码,即可完成子模板的渲染。下面以继承base.html的方式实现一个index.html文件,代码如下。

以上代码首先通过extends语法加载父模板,因为base.html和index.html是在同一级目录,所以直接写base.html。这里需要注意的是,extends必须出现在子模板所有代码的最前面。接下来分别实现了title、head、content这3个block,实现的block中的代码将会被自动填充到父模板指定的位置,并且最终会生成一个完整HTML结构的index.html文件。

模板中不能出现重名的block,如果一个地方需要用到另外一个block中的内容,可以使用self.blockname的方式进行引用,如以下代码所示。

    <title>
        {% block title %}
            这是标题
        {% endblock %}
    </title>
    <h1>{{ self.title() }}</h1>

在以上示例代码中,h1标签中通过{{ self.title() }}把title这个block中的内容引用到了h1标签中。

如果子模板要继承父模板中某个block的内容,如以上案例中,如果要继承父模板footer这个block中已有的内容,则可以通过super()来实现,示例代码如下。

    {% block footer %}
        {{ super() }}
        // 子模板自己的代码
    {% endblock %}

以上代码中,如果没有使用{{ super() }},那么子模板将不能继承父模板footer这个block中的内容。

4.4.3 引入模板

有些HTML模块需要在几个页面中使用,如果用模板继承会在所有子模板中都加载,不太合适;如果在每个需要使用这个HTML结构的页面中都重复相同的代码,会增加后期项目的维护成本。这时就可以通过include语法引入模板。如在网站中推广客服二维码联系方式,在有些页面中需要使用,但是并不是所有页面都需要,因此可以把相关代码写成_contact.html文件,然后在需要的位置进行引用即可,示例代码如下。

    {% include "_contact.xhtml" %}

因为_contact.html是作为被引用的模板而存在的,所以一般在命名前加一个下画线,这样可以和普通的页面模板区分开来。 AOv/SsaY7ml6usOwpHBjT+Gca8vEJCMoOfhuaE45yzreyvuM7mxBIZ4eKhS7w3OM

点击中间区域
呼出菜单
上一章
目录
下一章
×