文档章节

Flask的Jinja2模板引擎 - 块与宏

yehun
 yehun
发布于 2017/03/30 17:16
字数 2049
阅读 32
收藏 0

考虑到模板代码的重用,Jinja2提供了块 (Block)和宏 (Macro)的功能。块功能有些类似于C语言中的宏,原理就是代码替换;而宏的功能有些类似于函数,可以传入参数。本篇我们就来介绍下块和宏的用法。

块 (Block)

我们在子模板的开头定义了”{% extend ‘parent.html’ %}”语句来声明继承,此后在子模板中由”{% block block_name %}”和”{% endblock %}”所包括的语句块,将会替换父模板中同样由”{% block block_name %}”和”{% endblock %}”所包括的部分。
这就是块的功能,模板语句的替换。这里要注意几个点:
模板不支持多继承,也就是子模板中定义的块,不可能同时被两个父模板替换。
模板中不能定义多个同名的块,子模板和父模板都不行,因为这样无法知道要替换哪一个部分的内容。
另外,我们建议在”endblock”关键字后也加上块名,比如”{% endblock block_name %}”。虽然对程序没什么作用,但是当有多个块嵌套时,可读性好很多。
保留父模板块的内容
如果父模板中的块里有内容不想被子模板替换怎么办?我们可以使用”super( )”方法。我们将父模板”layout.html”改为:

<!doctype html>
<head>
    {% block head %}
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
    <title>{% block title %}{% endblock %}</title>
    {% endblock %}
</head>
<body>
    <div class="page">
    {% block body %}
    {% endblock %}
    </div>
</body>

并在子模板里,加上”head”块和”title”块:

{% block title %}Block Sample{% endblock %}
{% block head %}
    {{ super() }}
    <style type="text/css">
        h1 { color: #336699; }
    </style>
{% endblock %}

父模板同子模板的”head”块中都有内容。运行后,你可以看到,父模板中的”head”块语句先被加载,而后是子模板中的”head”块语句。这就得益于我们在子模板的”head”块中加上了表达式”{{ super( ) }}”。效果有点像Java中的”super( )”吧。

块内语句的作用域

默认情况下,块内语句是无法访问块外作用域中的变量。比如我们在”layout.html”加上一个循环:

{% for item in range(5) %}
    <li>{% block list %}{% endblock %}</li>
{% endfor %}

然后在子模板中定义”list”块并访问循环中的”item”变量:

{% block list %}
    <em>{{ item }}</em>
{% endblock %}

你会发现页面上什么数字也没显示。如果你想在块内访问这个块外的变量,你就需要在块声明时添加”scoped”关键字。比如我们在”layout.html”中这样声明”list”块即可:

{% for item in range(5) %}
    <li>{% block list scoped %}{% endblock %}</li>
{% endfor %}

宏 (Macro)

文章的开头我们就讲过,Jinja2的宏功能有些类似于传统程序语言中的函数,既然是函数就有其声明和调用两个部分。那就让我们先声明一个宏:

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

代码中,宏的名称就是”input”,它有三个参数分别是”name”, “type”和”value”,后两个参数有默认值。现在,让我们使用表达式来调用这个宏:

<p>{{ input('username', value='user') }}</p>
<p>{{ input('password', 'password') }}</p>
<p>{{ input('submit', 'submit', 'Submit') }}</p>

大家可以在页面上看到一个文本输入框,一个密码输入框及一个提交按钮。是不是同函数一样啊?其实它还有比函数更丰富的功能,之后我们来介绍。
访问调用者内容
我们先来创建个宏”list_users”:

{% macro list_users(users) -%}
  <table>
    <tr><th>Name</th><th>Action</th></tr>
    {%- for user in users %}
      <tr><td>{{ user.name |e }}</td>{{ caller() }}</tr>
    {%- endfor %}
  </table>
{%- endmacro %}

宏的作用就是将用户列表显示在表格里,表格每一行用户名称后面调用了”{{ caller( ) }}”方法,这个有什么用呢?先别急,我们来写调用者的代码:

{% set users=[{'name':'Tom','gender':'M','age':20},
              {'name':'John','gender':'M','age':18},
              {'name':'Mary','gender':'F','age':24}]
%}
 
{% call list_users(users) %}
    <td><input name="delete" type="button" value="Delete"></td>
{% endcall %}

与上例不同,这里我们使用了”{% call %}”语句块来调用宏,语句块中包括了一段生成”Delete”按钮的代码。运行下试试,你会发现每个用户名后面都出现了”Delete”按钮,也就是”{{ caller( ) }}”部分被调用者”{% call %}”语句块内部的内容替代了。不明觉厉吧!其实吧,这个跟函数传个参数进去没啥大区别,个人觉得,主要是有些时候HTML语句太复杂(如上例),不方便写在调用参数上,所以就写在”{% call %}”语句块里了。
Jinja2的宏不但能访问调用者语句块的内容,还能给调用者传递参数。嚯,这又是个什么鬼?我们来扩展下上面的例子。首先,我们将表格增加一列性别,并在宏里调用”caller()”方法时,传入一个变量”user.gender”:

{% macro list_users(users) -%}
  <table>
    <tr><th>Name</th><th>Gender</th><th>Action</th></tr>
    {%- for user in users %}
      <tr><td>{{ user.name |e }}</td>{{ caller(user.gender) }}</tr>
    {%- endfor %}
  </table>
{%- endmacro %}

然后,我们修改下调用者语句块:

{% call(gender) list_users(users) %}
    <td>
    {% if gender == 'M' %}
    <img src="{{ url_for('static', filename='img/male.png') }}" width="20px">
    {% else %}
    <img src="{{ url_for('static', filename='img/female.png') }}" width="20px">
    {% endif %}
    </td>
    <td><input name="delete" type="button" value="Delete"></td>
{% endcall %}

大家注意到,我们在使用”{% call %}”语句时,将其改为了”{% call(gender) … %}”,这个括号中的”gender”就是用来接受宏里传来的”user.gender”变量。因此我们就可以在”{% call %}”语句中使用这个”gender”变量来判断用户性别。这样宏就成功地向调用者传递了参数。

宏的内部变量

上例中,我们看到宏的内部可以使用”caller( )”方法获取调用者的内容。此外宏还提供了两个内部变量:
varargs
这是一个列表。如果调用宏时传入的参数多于宏声明时的参数,多出来的没指定参数名的参数就会保存在这个列表中。
kwargs
这是一个字典。如果调用宏时传入的参数多于宏声明时的参数,多出来的指定了参数名的参数就会保存在这个字典中。
让我们回到第一个例子input宏,在调用时增加其传入的参数,并在宏内将上述两个变量打印出来:

{% macro input(name, type='text', value='') -%}
    <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}">
    <br /> {{ varargs }}
    <br /> {{ kwargs }}
{%- endmacro %}
<p>{{ input('submit', 'submit', 'Submit', 'more arg1', 'more arg2', ext='more arg3') }}</p>

可以看到,varargs变量存了参数列表”[‘more arg1’, ‘more arg2’]”,而kwargs字典存了参数”{‘ext’:’more arg3′}”。

宏的导入

一个宏可以被不同的模板使用,所以我们建议将其声明在一个单独的模板文件中。需要使用时导入进来即可,而导入的方法也非常类似于Python中的”import”。让我们将第一个例子中”input”宏的声明放到一个”form.html”模板文件中,然后将调用的代码改为:

{% import 'form.html' as form %}
<p>{{ form.input('username', value='user') }}</p>
<p>{{ form.input('password', 'password') }}</p>
<p>{{ form.input('submit', 'submit', 'Submit') }}</p>

运行下,效果是不是同之前的一样?你也可以采用下面的方式导入:

{% from 'form.html' import input %}
<p>{{ input('username', value='user') }}</p>
<p>{{ input('password', 'password') }}</p>
<p>{{ input('submit', 'submit', 'Submit') }}</p>

包含 (Include)

这里我们再介绍一个Jinja2模板中代码重用的功能,就是包含 (Include),使用的方法就是”{% include %}”语句。其功能就是将另一个模板加载到当前模板中,并直接渲染在当前位置上。它同导入”import”不一样,”import”之后你还需要调用宏来渲染你的内容,”include”是直接将目标模板渲染出来。它同block块继承也不一样,它一次渲染整个模板文件内容,不分块。
我们可以创建一个”footer.html”模板,并在”layout.html”中包含这个模板:

<body>
    ...
    {% include 'footer.html' %}
</body>

当”include”的模板文件不存在时,程序会抛出异常。你可以加上”ignore missing”关键字,这样如果模板不存在,就会忽略这段”{% include %}”语句。

{% include 'footer.html' ignore missing %}

“{% include %}”语句还可以跟一个模板列表:

{% include ['footer.html','bottom.html','end.html'] ignore missing %}

上例中,程序会按顺序寻找模板文件,第一个被找到的模板即被加载,而其后的模板都会被忽略。如果都没找到,那整个语句都会被忽略。
 

© 著作权归作者所有

yehun
粉丝 8
博文 218
码字总数 137315
作品 0
长宁
高级程序员
私信 提问
Flask实例教程八

Flask本身是需要依靠Jinja2。其模板引擎也采用的是Jinja2。 本博文重点介绍一下Jinja2模板引擎的基本使用。 一:模板的基本使用 app.pyfrom flask import Flaskfrom flask import render_temp...

Jeff_Linux
2014/06/06
0
0
第21天: Web 开发 Jinja2 模板引擎

by 太阳雪 被之前的文章中,简单介绍了 Python Web 开发框架 Flask,知道了如何写个 Hello World,但是距离用 Flask 开发真正的项目,还有段距离,现在我们目标更靠近一些 —— 学习下 Jinj...

纯洁的微笑
09/24
0
0
Flask学习笔记-Flask模板集成Bootstrap

Flask模板集成Bootstrap。一般情况下Flask都是搭配Jinja2模板引擎来实现视图展现,不过现在Bootstrap比较流行,内置的样式也比较好看,有利于提高开发效率,本篇文章就是讲解在Flask如何集成...

ykbj
2016/02/22
1K
0
Python全栈 Web(Flask框架、变量、模板)

模板 Templates: 模板中的语法规范 变量: 变量时一种特殊的占位符 告诉模板引擎该位置的 值是从渲染模板时的数据中来获取的 在视图中: 模板

巴黎香榭
2018/09/21
0
0
微型Python框架--Flask

Flask 是一个微型的 Python 开发的 Web 框架,基于Werkzeug WSGI工具箱和Jinja2 模板引擎。 Flask使用BSD授权。 Flask也被称为“microframework”,因为它使用简单的核心,用extension增加其...

匿名
2010/05/06
32.8K
6

没有更多内容

加载失败,请刷新页面

加载更多

iptables删除命令中的相关问题

最近在做一个中间件的配置工作,在配置iptables的时候,当用户想删除EIP(即释放当前连接),发现使用iptables的相关命令会提示错误。iptables: Bad rule (does a matching rule exist in t...

xiangyunyan
35分钟前
2
0
IT兄弟连 HTML5教程 HTML5表单 新增的表单属性1

HTML5 Input表单为<form>和<input>标签添加了几个新属性,属性如表1。 1 autocomplete属性 autocomplete属性规定form或input域应该拥有自动完成功能,当用户在自动完成域中开始输入时,浏览器...

老码农的一亩三分地
今天
7
0
OSChina 周五乱弹 —— 葛优理论+1

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @这次装个文艺青年吧 :#今日歌曲推荐# 分享米津玄師的单曲《LOSER》: mv中的舞蹈诡异却又美丽,如此随性怕是难再跳出第二次…… 《LOSER》-...

小小编辑
今天
1K
19
nginx学习笔记

中间件位于客户机/ 服务器的操作系统之上,管理计算机资源和网络通讯。 是连接两个独立应用程序或独立系统的软件。 web请求通过中间件可以直接调用操作系统,也可以经过中间件把请求分发到多...

码农实战
今天
5
0
Spring Security 实战干货:玩转自定义登录

1. 前言 前面的关于 Spring Security 相关的文章只是一个预热。为了接下来更好的实战,如果你错过了请从 Spring Security 实战系列 开始。安全访问的第一步就是认证(Authentication),认证...

码农小胖哥
今天
16
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部