在odoo中,不管是前端的html页面,还是小部件的html片段,都是通过Qweb来进行模板渲染的。其实有关于html、xml的模板引擎有很多,只是在odoo中选用的是Qweb而已。所以这一节我们来详细介绍一下Qweb,给大家总结一个可以收藏备查的知识点。
首先Qweb是一个xml的模板引擎,为了区分正常的xml定义与模板的定义,所以在Qweb中设定以"t-"开头的属性是需要模板处理的内容,另外还有以"t"名称定义的占位元素节点,这类元素本身在渲染的时候不会生成html代码。
我们先来看一下怎么在模板中定义变量,并输出变量:
<t t-set="age" t-value="43"/><p><t t-esc="age"/></p>
第一行通过t-set来设置一个变量名age,后面的t-value表示这个变量的值。第二行输出一段html,通过t-esc属性来输出变量age的值,所以最后的输出内容是:
<p>43</p>
另外还有一个t-raw的输出定义,其与t-esc类似,只是不会对输出的内容进行html转义。
跟其它的模板引擎一样,Qweb也提供了用于渲染时的条件式判断和循环判断,我们先来看一下条件式判断的使用:
<div><t t-if="condition"><p>ok</p></t></div>
这是一个简单的条件式,通过t-if属性来进行定义,后面的condition是一个条件式,如果这个条件为true,则输出结果如下:
<div><p>ok</p></div>
前面我们说过<t>只是一个占位元素,在输出的时候不会生成对应html片段。当这个条件为false,则输出结果如下:
<div></div>
我们也可以再做一些简化,不需要定义<t>元素,直接用如下的方法来实现:
<div><p t-if="condition">ok</p></div>
Qweb还提供了比较复杂一点的t-elif和t-else属性,如下的方法:
<div><p t-if="user.birthday == today()">Happy birthday!</p><p t-elif="user.login == 'root'">Welcome master!</p><p t-else="">Welcome!</p></div>
首先Qweb会判断第一个条件,如果成立,则输出第一行,否则判断第二个条件,如果第二行条件成立,则输出第二行,前面条件如果都不成立,则输出第三行。
上面的t-elif可以有多个,而且还可以嵌套使用。如下图所示:

上图首先根据payment_status的不同状态进行了不同的处理,然后当payment_status为done时,又嵌套了一个t-if和t-else来进行不同的处理,一般在嵌套时,为了区分代码块的层次,这里会用到<t>元素来进行区分。
对于模板中的循环情况,QWeb也提供了一个t-foreach属性,该属性使用一个表达式返回要迭代的集合,第二个属性t-as提供用于迭代的“当前项”的名称:
<t t-foreach="[1, 2, 3]" t-as="i"><p><t t-esc="i"/></p></t>
上面的实例表示这是一个需要执行三次的循环,每次循环时,变量i分别会赋值为1,2,3,所以输出的时候,就会是如下的结果:
<p>1</p><p>2</p><p>3</p>
除了通过t-as传递的名称外,foreach还为各种情况提供了一些其他变量,我们常用的会有如下几个:
$as_index:当前迭代索引(迭代的第一项的索引为0)
$as_first:当前项目是否是迭代的第一个
$as_last:当前项目是否为迭代的最后一个
在t-foreach代码块中,也可以通过t-set来定义变量,这里有一个关系到变量作用域的问题,如果变量是在代码块中新创建的,则作用域只在代码块中有效,如果是在上下文环境定义的变量,在代码块中有重新赋值,同在代码块结束后,将重新改变上下文环境中的变量值,如下所示:
<t t-set="existing_variable" t-value="False"/><!-- 变量existing_variable设置为false --><p t-foreach="[1, 2, 3]" t-as="i"><t t-set="existing_variable" t-value="True"/><t t-set="new_variable" t-value="True"/><!-- 变量existing_variable和new_variable设置为true--></p><!-- 变量existing_variable的值还是为true--><!-- 变量new_variable在这里是undefined -->
Qweb可以通过计算获取元素的属性并输出到html片段中,这个主要是通过设置"t-att"属性来实现,主要的方式有三种:
t-att-$name:创建一个名为$name的属性,并将结果设置为该属性的值,如:
<div t-att-a="42"/>
输出的结果为:
<div a="42"></div>
t-attf-$name:与上一个相同,但参数是格式字符串,而不仅仅是表达式,如:
<t t-foreach="[1, 2, 3]" t-as="item"><li t-attf-class="row {{ (item_index % 2 === 0) ? 'even' : 'odd' }}"><t t-esc="item"/></li></t>
输出结果为:
<li class="row even">1</li><li class="row odd">2</li><li class="row even">3</li>
t-att=mapping:如果参数是映射,则每个(键,值)对都会生成一个新属性及其值:
<div t-att="{'a': 1, 'b': 2}"/>
输出结果为:
<div a="1" b="2"></div>
t-att=pair:如果参数是2个元素的元组或数组,则第一项是属性的名称,第二项是值:
<div t-att="['a', 'b']"/>
输出结果为:
<div a="b"></div>
Qweb适用于通过模版来渲染html页面,实际应用中,有很多的页面可能大部分使用的模版是相同,只有少量差异。为了增加模版的通用性,我们可以把共用的部分的定义成一个个不同的模版,然后通过搭积木的方式,把几个不同的模板组合成一个新的页面。这里就关系到一个问题,怎么在一个模板中调用另一个模板?在Qweb中提供了一个指令t-call来操作,如:
<t t-call="other-template"/>
我们假设other-template名称的定义为:
<p><t t-value="var"/></p>
通过正常的方式渲染时,得到的结果是"<P></P>",因为渲染的时候变量var没有值。那我们再来看通过如下方式来调用:
<t t-set="var" t-value="1"/><t t-call="other-template"/>
这个时候返回的结果就是"<p>1</p>",所以这里我们可以看出,当前模板的变量定义作用域,是可以传导到被call的子模版中的。在这个例子中,变量var是一个全局的变量,除了可以在子模版中使用以外,在当前模板的其它地方也可以使用,有时候只想这个变量在子模板中使用,怎么办呢?我们也可以使用如下的方法,来指定变量只作用于子模板的调用范围:
<t t-call="other-template"><t t-set="var" t-value="1"/></t><!-- 在这个位置,变量"var"是没有值的 -->
上面的例子中,call节点里面不仅仅是只能有t-set来赋值,也可以是任意的html片段,这段html会被赋值为一个特殊的变量"0",然后在子模版中可以通过"<t t-raw="0"/>来获取这一段代码。
比如other-template的定义是:
<div>This template was called with content:<t t-raw="0"/></div>
我们通过如下方式来调用:
<t t-call="other-template"><em>content</em></t>
那我们得到的结果就是:
<div>This template was called with content:<em>content</em></div>
QWeb的大多数Python端用法是在控制器中(以及HTTP请求期间),在这种情况下,可以通过调用odoo.http.HttpRequest.render()来简单地呈现数据库中存储的模板:
response = http.request.render('my-template', {'context_value': 42})
最后,我们再来看一下怎么在odoo中定义一个新的模版:
<templates><t t-name="template-name"><!-- template code --></t></templates>
t-name用来指定一个模版的名称,是任意的一个字符串,不会有其它的参数。在关联多个模板(例如称为子模板)时,习惯上使用点分隔的名称来表示层次关系。
在实际应用中,我们可以重新创建一个新的模版,也可以通过继承其它的模板,做出少量修改后来生成新的模板。模板的继承是通过t-extend来进行实现:
<t t-extend="base.template"><t t-jquery="ul" t-operation="append"><li>new element</li></t></t>
上面的定义没有使用t-name来指定新的模板名称,所以这个时候会直接修改base.template的内容,继承后的修改是通过t-jquery来定位节点内的元素,t-operation来决定修改方式。
t-jquery是一个CSS选择器,通过此选择器来获取指定的操作节点,t-operation有如下几种方式,来决定新的片段操作:
append:节点的主体附加在上下文节点的末尾(在上下文节点的最后一个子节点之后)
prepend:该节点的主体位于上下文节点之前(在上下文节点的第一个子节点之前插入)
before:该节点的主体被插入到上下文节点之前
after:该节点的主体被插入到上下文节点之后
inner:节点的主体替换了上下文节点的子节点
replace:节点的主体用于替换上下文节点本身
attributes:节点的主体应该是任意数量的属性元素,每个属性元素都具有name属性和一些文本内容,上下文节点的name属性将设置为指定的值(如果已经存在则替换为该值,或者如果不存在则添加)
以上是有关于Odoo中的Qweb知识点,建议大家可以收藏,以便需要的时候随时查看。




