QWeb模板

QWeb是Odoo[#othertemplates]_使用的主要模板引擎。它是一个XML模板引擎1,主要用于生成HTML_片段和页面。

模板指令被指定为以 t- 为前缀的 XML 属性,例如 条件语句t-if,元素和其他属性直接呈现。

为了避免元素渲染,还提供了一个占位符元素 <t> ,它执行其指令但不会自行生成任何输出:

<t t-if="condition">
    <p>Test</p>
</t>

将导致:

<p>Test</p>

如果 condition 为真,但是:

<div t-if="condition">
    <p>Test</p>
</div>

将导致:

<div>
    <p>Test</p>
</div>

数据输出

QWeb 的输出指令 out 会自动对其输入进行 HTML 转义,在显示用户提供的内容时限制 XSS 风险。

out takes an expression, evaluates it and injects the result in the document:

<p><t t-out="value"/></p>

使用值 value 设置为 42 渲染的结果为:

<p>42</p>

请参阅 高级输出 以获取更高级的主题(例如注入原始HTML等)。

条件语句

QWeb 有一个条件指令 if,它评估给定的表达式作为属性值:

<div>
    <t t-if="condition">
        <p>ok</p>
    </t>
</div>

如果条件为真,则呈现该元素:

<div>
    <p>ok</p>
</div>

但如果条件为假,则从结果中删除它:

<div>
</div>

条件渲染适用于指令的承载者,它不必是 <t>:

<div>
    <p t-if="condition">ok</p>
</div>

将会得到与前一个示例相同的结果。

还有额外的条件分支指令 t-elift-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有一个迭代指令 foreach ,它接受一个返回要迭代的集合的表达式和第二个参数 t-as ,用于提供迭代的”当前项目”的名称:

<t t-foreach="[1, 2, 3]" t-as="i">
    <p><t t-out="i"/></p>
</t>

将被渲染为:

<p>1</p>
<p>2</p>
<p>3</p>

与条件类似, foreach 应用于带有指令属性的元素,并且

<p t-foreach="[1, 2, 3]" t-as="i">
    <t t-out="i"/>
</p>

与前一个示例等效。

foreach can iterate on an array (the current item will be the current value) or a mapping (the current item will be the current key). Iterating on an integer (equivalent to iterating on an array between 0 inclusive and the provided integer exclusive) is still supported but deprecated.

除了通过 t-as 传递的名称外, foreach 还提供了一些其他变量用于不同的数据点:

警告

$as 将被替换为传递给 t-as 的名称

$as_all (deprecated)

正在迭代的对象

注解

此变量仅在JavaScript QWeb中可用,而不在Python中可用。

$as_value

当前迭代的值,对于列表和整数与 $as 相同,但对于映射,它提供值(而 $as 提供键)

$as_index

当前迭代的索引(第一个迭代项的索引为0)

$as_size

如果可用,返回集合的大小

$as_first

当前项是否为迭代的第一个项(等同于 $as_index == 0)

$as_last

当前项是否为迭代的最后一项(等同于 $as_index + 1 == $as_size),需要迭代器的大小可用

$as_parity (deprecated)

either "even" or "odd", the parity of the current iteration round

$as_even (deprecated)

一个布尔标志,指示当前迭代轮次是否在偶数索引上

$as_odd (deprecated)

一个布尔标志,指示当前迭代轮次是否为奇数索引

这些额外提供的变量和在 foreach 中创建的所有新变量只在 foreach 的范围内可用。如果变量存在于 foreach 的上下文之外,其值将在foreach结束时复制到全局上下文中。

<t t-set="existing_variable" t-value="False"/>
<!-- existing_variable now 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 and new_variable now True -->
</p>

<!-- existing_variable always True -->
<!-- new_variable undefined -->

属性

QWeb可以动态计算属性并将计算结果设置为输出节点的属性。这是通过 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-out="item"/>
    </li>
</t>

将被渲染为:

<li class="row even">1</li>
<li class="row odd">2</li>
<li class="row even">3</li>

小技巧

有两种等效的格式字符串语法: "plain_text {{code}}" (也称为 jinja 风格)和 "plain_text #{code}" (也称为 ruby 风格)。

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允许在模板内创建变量,以记忆化计算(以便多次使用),为数据赋予更清晰的名称,等等。

这可以通过 set 指令完成,该指令需要提供要创建的变量名称。可以通过两种方式提供要设置的值:

  • 一个包含表达式的 t-value 属性,其评估结果将被设置为:

    <t t-set="foo" t-value="2 + 1"/>
    <t t-out="foo"/>
    

    将打印 3

  • 如果没有 t-value 属性,则节点的主体将被渲染并设置为变量的值:

    <t t-set="foo">
        <li>ok</li>
    </t>
    <t t-out="foo"/>
    

调用子模板

QWeb模板可用于顶层渲染,也可以使用 t-call 指令从另一个模板中使用(以避免重复或为模板的部分命名):

<t t-call="other-template"/>

如果 other_template 被定义为以下内容,则使用父级的执行上下文调用命名模板:

<p><t t-value="var"/></p>

上述调用将呈现为 <p/> (无内容),但是:

<t t-set="var" t-value="1"/>
<t t-call="other-template"/>

将被渲染为 <p>1</p>

However, this has the problem of being visible from outside the t-call. Alternatively, content set in the body of the call directive will be evaluated before calling the sub-template, and can alter a local context:

<t t-call="other-template">
    <t t-set="var" t-value="1"/>
</t>
<!-- "var" does not exist here -->

The body of the call directive can be arbitrarily complex (not just set directives), and its rendered form will be available within the called template as a magical 0 variable:

<div>
    This template was called with content:
    <t t-out="0"/>
</div>

被这样调用:

<t t-call="other-template">
    <em>content</em>
</t>

将导致:

<div>
    This template was called with content:
    <em>content</em>
</div>

高级输出

默认情况下, out 应该对需要转义的内容进行 HTML 转义,以保护系统免受 XSS 攻击。

Content which does not need to be escaped will instead be injected as-is in the document, and may become part of the document’s actual markup.

唯一的跨平台“安全”内容是 t-call 或使用 t-set 与“body”(而不是 t-valuet-valuef)一起使用的输出。

Python

通常情况下,您不必过于关心:对于需要生成“安全”内容的API,应自动生成,事情应该透明地运行。

对于需要更清晰的情况,以下 API 输出安全内容,这些内容默认情况下不会在注入模板时被重新转义:

  • HTML fields.

  • html_escape()markupsafe.escape() (它们是 别名,没有双重转义的风险)。

  • html_sanitize().

  • markupsafe.Markup.

    警告

    markupsafe.Markup 是一个不安全的 API,它是一个 断言,你希望内容是标记安全的,但不能保证,应该小心使用。

  • to_text() 不会将内容标记为安全,但不会从安全内容中删除该信息。

强制双重转义

如果内容被标记为安全的,但由于某些原因仍需要进行转义(例如打印HTML字段的标记),则可以将其转换回普通字符串以“去除”安全标志,例如在Python中使用 str(content) ,在Javascript中使用 String(content)

注解

Because Markup is a much richer type than Markup(), some operations will strip the safety information from a Markup() but not a Markup e.g. string concatenation ('' + content) in Python will result in a Markup with the other operand having been properly escaped, while in Javascript will yield a String() where the other operand was not escaped before the concatenation.

已弃用的输出指令

esc

An alias for out, would originally HTML-escape its input. Not yet formally deprecated as the only difference between out and esc is that the latter is a bit unclear / incorrect.

raw

A version of out which never escapes its content. Content is emitted as-is, whether it’s safe or not.

15.0 版后已移除: 使用 outmarkupsafe.Markup 值代替。

t-raw was deprecated because as the code producting the content evolves it can be hard to track that it’s going to be used for markup, leading to more complicated reviews and more dangerous lapses.

Python

独占指令

资源包

“智能记录”字段格式化

The t-field directive can only be used when performing field access (a.b) on a “smart” record (result of the browse method). It is able to automatically format based on field type, and is integrated in the website’s rich text editing.

t-options 可用于自定义字段,最常见的选项是 widget,其他选项取决于字段或小部件。

调试

t-debug

with an empty value, invokes the breakpoint() builtin function, which usually invokes a debugger (pdb by default).

The behaviour can be configured via PYTHONBREAKPOINT or sys.breakpointhook().

渲染缓存:

t-cache="key_cache" tags part of template to be cached at rendering time. Every sub-directives will be call only during the first rendering. It means that the sql queries excecuted during the rendering of those sub-directives are also done only once.

t-nocache="documentation" tags part of template to be render every time. The content can only use the root values.

为什么和何时使用 t-cache?

该指令用于加速渲染,通过缓存最终文档的部分内容,可以避免对数据库的查询。但是,应该谨慎使用 t-cache ,因为它会不可避免地使模板变得复杂(例如,它们对 t-set 的理解)。

然而,为了真正节省数据库查询,可能需要使用惰性求值的值来渲染模板。如果这些惰性求值的值用于缓存部分,则如果该部分可用于缓存,则不会对其进行求值。

The t-cache directive is useful for template parts using values that depend on a limited amount of data. We recommend to analyze the rendering of the template with the profiler (by activating the “Add qweb directive context” option). Passing lazy values to the rendering in controllers allow you to display the directives using these values and triggering the queries.

使用这样的缓存的一个问题是使其对不同的用户可用(不同的用户应该以相同的方式呈现缓存的部分)。另一个潜在问题是在必要时使其条目无效。对于后者,应明智地选择 键表达式 。例如,使用记录集的 write_date 可以使缓存键过时,而无需显式地从缓存中丢弃它。

还应注意到 t-cache 部分中的值是有作用域的。这意味着,如果在模板的这部分中有 t-set 指令,则在其后面呈现的内容可能与没有 t-cache 指令时不同。

如果在 t-cache 内部有一个 t-cache 会怎么样?

这些部分被缓存。每个部分只包含其渲染对应的字符串。因此,内部的 t-cache 可能会被较少读取,其缓存键不一定会被使用。如果必须这样做,则可能需要添加一个 t-nocache (在同一节点或父节点上)。

t-nocache 用于什么?

如果您想使用 t-cache 缓存模板的一部分,但是某个小部分必须保持动态并在缓存时进行评估。然而,在 t-nocache 中的部分将无法访问模板的 t-set 值。只有控制器提供的值可以在那里访问。例如,菜单被缓存,因为它始终是相同的,并且需要时间来渲染(使用 qweb 上下文的性能开发工具可以进行调查)。然而,在菜单中,我们希望电子商务购物车始终保持最新状态。因此,有一个 t-nocache 来保持此部分动态。

t-cache 的基础

The t-cache directive allows you to store the rendered result of a template. The key expression (eg 42: t-cache="42") will be evaluated as a python expression. This will be used to generate the cache key. So there can be different cache values (cached render part) for the same template part. If the key expression is a tuple or a list, it will be searched when generating the cache key. If one or more recordsets are returned by the key expression, then the model, ids and their corresponding write_date will be used to generate the cache key. Special case: If the key expression returns a Falsy value, then the content will not be cached.

示例:

<div t-cache="record,bool(condition)">
    <span t-if="condition" t-field="record.partner_id.name">
    <span t-else="" t-field="record.partner_id" t-options-widget="contact">
</div>

在这种情况下,缓存中可能存在与已返回的每个记录对应的值(字符串),包括 true 条件和 false 条件。如果一个模块修改了记录,修改了 write_date,缓存的值将被丢弃。

t-cache 和作用域值 (t-set, t-foreach…)

Values in t-cache are scoped, this involves a change in behavior between having or not having t-cache on one of the parent nodes. Don’t forget to take into account that Odoo uses a lot of templates, t-call and view inheritance. Adding a t-cache can therefore modify the rendering of a template that you do not see when editing. (t-foreach it’s like a t-set for each iteration)

示例:

<div>
    <t t-set="a" t-value="1"/>
    <inside>
        <t t-set="a" t-value="2"/>
        <t t-out="a"/>
    </inside>
    <outside t-out="a"/>

    <t t-set="b" t-value="1"/>
    <inside t-cache="True">
        <t t-set="b" t-value="2"/>
        <t t-out="b"/>
    </inside>
    <outside t-out="b"/>
</div>

将渲染:

<div>
    <inside>2</inside>
    <outside>2</inside>

    <inside>2</inside>
    <outside>1</inside>
</div>

t-nocache 的基础

在带有 t-nocache 属性的节点中包含的模板部分不会被缓存。因此,此内容是 动态的 ,会被系统地渲染。但是,可用的值是由控制器(调用 _render 方法时)提供的。

示例:

<section>
    <article t-cache="record">
        <title><t t-out="record.name"/> <i t-nocache="">(views: <t t-out="counter"/>)</i></titlle>
        <content t-out="record.description"/>
    </article>
</section>

将渲染(计数器=1):

<section>
    <article>
        <title>The record name <i>(views: 1)</i></titlle>
        <content>Record description</content>
    </article>
</section>

这里包含容器的 <i> 标签将始终被呈现,而其余部分将作为缓存中的单个字符串。

t-nocache 和作用域根值 (t-set, t-foreach…)

The contents of the t-nocache tag can be used for documentation and to explain why the directive is added. The values are scoped into t-nocache, these values are root values only (values provided by the controller and/or when calling the _render method of ir.qweb). t-set can be done in the template part, but will not be available elsewhere.

示例:

<section>
    <t t-set="counter" t-value="counter * 10"/>
    <header t-nocache="">
        <t t-set="counter" t-value="counter + 5"/>
        (views: <t t-out="counter"/>)
    </header>
    <article t-cache="record">
        <title><t t-out="record.name"/> <i t-nocache="">(views: <t t-out="counter"/>)</i></titlle>
        <content t-out="record.description"/>
    </article>
    <footer>(views: <t t-out="counter"/>)</footer>
</section>

将渲染(计数器=1):

<section>
    <header>
        (views: 6)
    </header>
    <article>
        <title>The record name <i>(views: 1)</i></titlle>
        <content>Record description</content>
    </article>
    <footer>(views: 10)</footer>
</section>

在这里,包含容器的 <i> 标签将始终被渲染。而其余部分将作为缓存中的单个字符串。计数器不会被 t-set 之外的 t-nocache 更新。

t-nocache-* 在缓存中添加一些基本值

In order to be able to use values generated in the template, it is possible to cache them. The directive is used as t-nocache-*="expr" where * is the name of the chosen value and expr the python expression so the result will be cached. The cached value must be primitive type.

示例:

<section t-cache="records">
    <article t-foreach="records" t-as="record">
        <header>
            <title t-field="record.get_method_title()"/>
        </header>
        <footer t-nocache="This part has a dynamic counter and must be rendered all the time."
                t-nocache-cached_value="record.get_base_counter()">
            <span t-out="counter + cached_value"/>
        </footer>
    </article>
</section>

变量 cached_value 被缓存,使用了 t-cache="records" 的缓存模板部分,并且每次都会添加到作用域根值中。

辅助函数

基于请求的

Most Python-side uses of QWeb are in controllers (and during HTTP requests), in which case templates stored in the database (as views) can be trivially rendered by calling odoo.http.HttpRequest.render():

response = http.request.render('my-template', {
    'context_value': 42
})

这将自动创建一个 Response 对象,可以从控制器返回(或进一步自定义以适应)。

基于视图的

比起之前的帮助程序,更深层次的是在 ir.qweb 上使用 _render 方法(使用数据表),以及公共模块方法 render (不使用数据库):

_render(id[, values])

通过数据库ID或 外部ID 渲染QWeb视图/模板。模板会自动从 ir.qweb 记录中加载。

_prepare_environment method sets up a number of default values in the rendering context. The http_routing and website addons, also default values they need. You can use minimal_qcontext=False option to avoid this default value like the public method render:

request

当前的 Request 对象(如果有的话)

debug

当前请求(如果有)是否处于 调试 模式

quote_plus

URL编码实用函数

json

相应的标准库模块

time

相应的标准库模块

datetime

相应的标准库模块

relativedelta

查看模块

keep_query

the keep_query helper function

参数
  • values – 传递给 QWeb 用于渲染的上下文值

  • engine (str) – 用于渲染的Odoo模型名称,可用于在本地扩展或自定义QWeb(通过基于 ir.qweb 创建“新”QWeb并进行更改)

render(template_name, values, load, **options)
load(ref)()

返回 etree 对象,引用

Javascript

独占指令

定义模板

The t-name directive can only be placed at the top-level of a template file (direct children to the document root):

<templates>
    <t t-name="template-name">
        <!-- template code -->
    </t>
</templates>

它不需要其他参数,但可以与 <t> 元素或任何其他元素一起使用。对于 <t> 元素, <t> 应该只有一个子元素。

模板名称是任意字符串,但是当多个模板相关联(例如称为子模板)时,通常使用点分隔的名称来表示层次关系。

模板继承

模板继承用于以下两种情况:
  • 修改现有模板,例如向模板添加信息

由其他模块创建。
  • 从给定的父模板创建一个新模板

模板继承通过使用两个指令来执行:
  • t-inherit which is the name of the template to inherit from,

  • t-inherit-mode 是继承的行为方式:可以将其设置为 primary 以从父模板创建新的子模板,或者将其设置为 extension 以直接修改父模板。

还可以指定可选的 t-name 指令。如果在主模式下使用,它将是新创建的模板的名称,否则它将作为注释添加到转换后的模板中,以帮助追溯继承关系。

对于继承本身,更改是使用xpaths指令完成的。请参阅XPATH_文档以获取可用指令的完整集合。

主要继承(子模板):

<t t-name="child.template" t-inherit="base.template" t-inherit-mode="primary">
    <xpath expr="//ul" position="inside">
        <li>new element</li>
    </xpath>
</t>

扩展继承(原地转换):

<t t-inherit="base.template" t-inherit-mode="extension">
    <xpath expr="//tr[1]" position="after">
        <tr><td>new cell</td></tr>
    </xpath>
</t>

旧继承机制(已弃用)

模板继承是通过 t-extend 指令执行的,该指令以要更改的模板名称作为参数。

当与 t-name 结合使用时,指令 t-extend 将作为主要继承;当单独使用时,将作为扩展继承。

在这两种情况下,都可以使用任意数量的 t-jquery 子指令来执行修改操作:

<t t-extend="base.template">
    <t t-jquery="ul" t-operation="append">
        <li>new element</li>
    </t>
</t>

The t-jquery directives takes a CSS selector. This selector is used on the extended template to select context nodes to which the specified t-operation is applied:

append

节点的主体被追加到上下文节点的末尾(在上下文节点的最后一个子节点之后)

prepend

节点的主体被插入到上下文节点之前(插入到上下文节点的第一个子节点之前)

before

节点的主体被插入到上下文节点之前

after

节点的主体被插入到上下文节点之后

inner

节点的主体替换上下文节点的子节点

replace

节点的主体用于替换上下文节点本身

attributes

节点的主体应该是任意数量的 attribute 元素,每个元素都有一个 name 属性和一些文本内容,上下文节点的命名属性将被设置为指定的值(如果已存在,则被替换,如果不存在,则被添加)

没有操作

如果未指定 t-operation ,则模板主体将被解释为javascript代码,并以上下文节点作为 this 执行

警告

虽然比其他操作更强大,但这种模式也更难调试和维护,建议避免使用

调试

javascript QWeb 实现提供了一些调试钩子:

t-log

接受一个表达式参数,在渲染过程中评估该表达式,并使用 console.log 记录其结果:

<t t-set="foo" t-value="42"/>
<t t-log="foo"/>

将在控制台打印 42

t-debug

在模板渲染期间触发调试器断点:

<t t-if="a_test">
    <t t-debug=""/>
</t>

如果调试处于活动状态,将停止执行(确切条件取决于浏览器及其开发工具)

t-js

节点的主体是在模板渲染期间执行的JavaScript代码。接受一个 context 参数,该参数是渲染上下文在 t-js 主体中可用的名称:

<t t-set="foo" t-value="42"/>
<t t-js="ctx">
    console.log("Foo is", ctx.foo);
</t>

辅助函数

core.qweb

(core is the web.core module) An instance of QWeb2.Engine() with all module-defined template files loaded, and references to standard helper objects _ (underscore), _t (translation function) and JSON.

core.qweb.render 可以用于轻松渲染基本模块模板

API

class QWeb2.Engine()

QWeb的“渲染器”处理大部分QWeb的逻辑(加载、解析、编译和渲染模板)。

Odoo Web 在核心模块中为用户实例化一个,并将其导出到 core.qweb。它还将各个模块的所有模板文件加载到该 QWeb 实例中。

一个 QWeb2.Engine() 也可以作为一个 “template namespace”。

QWeb2.Engine.QWeb2.Engine.render(template[, context])

使用 context (如果提供)将预先加载的模板呈现为字符串,以查找在模板呈现期间访问的变量(例如要显示的字符串)。

参数
  • template (String()) – 要渲染的模板名称

  • context (Object()) – 用于模板渲染的基本命名空间

返回

字符串

The engine exposes an other method which may be useful in some cases (e.g. if you need a separate template namespace with, in Odoo Web, Kanban views get their own QWeb2.Engine() instance so their templates don’t collide with more general “module” templates):

QWeb2.Engine.QWeb2.Engine.add_template(templates)

在 QWeb 实例中加载一个模板文件(一组模板)。模板可以指定为:

一个 XML 字符串

QWeb 将尝试将其解析为 XML 文档,然后加载它。

一个URL

QWeb将尝试下载URL内容,然后加载生成的XML字符串。

一个 DocumentNode

QWeb将遍历文档的第一层(提供的根节点的子节点),并加载任何命名模板或模板覆盖。

一个 QWeb2.Engine() 还公开了各种属性以进行行为定制:

QWeb2.Engine.QWeb2.Engine.prefix

用于在解析过程中识别指令的前缀。一个字符串。默认为 t

QWeb2.Engine.QWeb2.Engine.debug

布尔标志将引擎置于“调试模式”。通常,QWeb在模板执行期间拦截任何错误。在调试模式下,它允许所有异常通过而不拦截它们。

QWeb2.Engine.QWeb2.Engine.jQuery

在模板继承处理期间使用的 jQuery 实例。默认为 window.jQuery

QWeb2.Engine.QWeb2.Engine.preprocess_node

一个 Function。如果存在,在编译每个DOM节点到模板代码之前调用。在Odoo Web中,这用于自动翻译模板中的文本内容和一些属性。默认为 null

1

它与Genshi_相似,但它不使用(也不支持) XML namespaces _

2

尽管出于历史原因或者因为它们更适合特定的用例,Odoo使用了一些其他的模板引擎。但是Odoo 9.0仍然依赖于Jinja_和Mako