QWeb 模板

QWeb 是 Odoo2 使用的主要模板引擎。它是一个 XML 模板引擎1,主要用于生成 HTML 片段和页面。

模板指令被指定为 XML 属性,前缀为 t-`, for instance ``t-if``(对于 条件句),元素和其他属性直接呈现。

为了避免元素渲染,还可以使用占位符元素“<t>”,它执行其指令但本身不生成任何输出:

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

将导致:

<p>Test</p>

如果 condition 为 true,但是:

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

将导致:

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

数据输出

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

out 接受一个表达式,对其求值并将结果注入到文档中:

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

使用值 value set to 42 渲染会产生:

<p>42</p>

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

条件句

QWeb 有一个条件指令 if,它计算作为属性值给出的表达式:

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

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

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

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

<div>
</div>

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

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

将给出与前面的示例相同的结果。

额外的条件分支指令 t-elif and 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 有一个迭代指令 foreach which take an expression returning the collection to iterate on, and a second parameter 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 可以迭代数组(当前项将是当前值)或映射(当前项将是当前键)。仍然支持对整数进行迭代(相当于对介于 0 和提供的整数之间的数组进行迭代),但已弃用。

除了通过“t-as`, ``foreach`”传递的名称之外,还为各种数据点提供了一些其他变量:

警告

$as will be replaced by the name passed to t-as

:samp:`{$as}_all`(已弃用)

被迭代的对象

注解

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

$as_value

当前迭代值,与“$as` for lists and integers, but for mappings it provides the value (where ``$as`”相同,提供了密钥)

$as_index

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

$as_size

集合的大小(如果可用)

$as_first

当前项是否是迭代的第一项(相当于 $as_index == 0

$as_last

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

:samp:`{$as}_parity`(已弃用)

要么 "even" or "odd",当前迭代轮次的奇偶校验

:samp:`{$as}_even`(已弃用)

一个布尔标志,指示当前迭代轮位于偶数索引上

:samp:`{$as}_odd`(已弃用)

一个布尔标志,指示当前迭代轮位于奇数索引上

提供的这些额外变量以及创建到 foreach are only available in the scope of the foreach. If the variable exists outside the context of the 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”(属性)指令完成的,该指令以 3 种不同的形式存在:

t-att-$name

创建名为“$name”的属性,评估属性值并将结果设置为属性的值:

<div t-att-a="42"/>

将呈现为:

<div a="42"></div>
t-attf-$name

与之前相同,但参数是 format string 而不仅仅是一个表达式,对于混合文字和非文字字符串(例如类)通常很有用:

<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}}" (aka jinja-style) and ``”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>”。

但是,这存在从外部可见的问题,“t-call`. Alternatively, content set in the body of the ``call`”指令将在调用子模板之前进行评估,并且可以更改本地上下文:

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

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 变量的主体:

<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

不需要转义的内容将按原样注入到文档中,并且可能成为文档实际标记的一部分。

唯一的跨平台“安全”内容是与“主体”一起使用的 t-callt-set 的输出(而不是 t-value or t-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)

注解

因为 Markup 是比 Markup() 丰富得多的类型,所以某些操作会从 Markup() 中删除安全信息,但不会从 Markup 中删除,例如Python 中的字符串连接('' + content)将产生 Markup ,而另一个操作数已被正确转义,而在 Javascript 中将产生 String() ,其中另一个操作数在连接之前*未*转义。

已弃用的输出指令

esc

out, would originally HTML-escape its input. Not yet formally deprecated as the only difference between out and esc 的别名是后者有点不清楚/不正确。

raw

out 的一个版本*永远不会*转义其内容。内容按原样发出,无论安全与否。

15.0 版后已移除: 请使用带有 markupsafe.Markup 值的“out”。

t-raw 已被弃用,因为随着代码*生产*内容的发展,很难跟踪它将用于标记,从而导致更复杂的审查和更危险的失误。

Python

独家指令

资产包

“智能记录”字段格式

t-field directive can only be used when performing field access (a.b) on a “smart” record (result of the browse 方法)。它能够根据字段类型自动格式化,并集成在网站的富文本编辑中。

t-options can be used to customize fields, the most common option is widget,其他选项取决于字段或小部件。

调试

t-debug

使用空值时,调用 breakpoint() 内置函数,该函数通常调用调试器(默认情况下为 pdb )。

可以通过 PYTHONBREAKPOINTsys.breakpointhook() 配置该行为。

帮手

基于请求

QWeb 的大多数 Python 端使用都在控制器中(以及在 HTTP 请求期间),在这种情况下,存储在数据库中的模板(如 views)可以通过调用 odoo.http.HttpRequest.render() 轻松呈现:

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

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

基于视图

比前一个帮助器更深的层次是``_render`` method on ir.qweb (use the datable) and the public module method ``render``(不使用数据库):

_render(id[, values])

通过数据库 ID 或 external 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

当前请求(如果有)是否处于``debug``模式

quote_plus

url 编码实用函数

json

对应的标准库模块

time

对应的标准库模块

datetime

对应的标准库模块

relativedelta

参见模块

keep_query

keep_query 辅助函数

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

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

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

返回 etree 对象,ref

JavaScript

独家指令

定义模板

t-name 指令只能放置在模板文件的顶层(将子项定向到文档根目录):

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

它不需要其他参数,但可以与“<t>` element or any other. With a <t> element, the ``<t>`”一起使用,应该有一个子项。

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

模板继承

模板继承用于:
  • 就地更改现有模板,例如将信息添加到模板

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

模板继承是通过使用两个指令来执行的:
  • t-inherit 是要继承的模板的名称,

  • t-inherit-mode which is the behaviour of the inheritance: it can either be set to primary to create a new child template from the parented one or to 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-extend will act as a primary inheritance when combined with t-name 以及单独使用时作为扩展指令。

在这两种情况下,都会使用任意数量的“t-jquery”子指令来执行更改:

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

应用 t-jquery directives takes a CSS 选择器. This selector is used on the extended template to select context nodes to which the specified t-operation

append

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

prepend

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

before

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

after

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

inner

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

replace

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

attributes

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

无操作

如果没有``t-operation`` is specified, the template body is interpreted as javascript code and executed with the context node as 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 parameter, which is the name under which the rendering context will be available in the t-js 的主体:

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

帮手

core.qweb

(核心是``web.core`` module) An instance of QWeb2.Engine() with all module-defined template files loaded, and references to standard helper objects _ (underscore), ``_t``(翻译函数)和JSON_.

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

应用程序编程接口

class QWeb2.Engine()

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

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

A QWeb2.Engine() 也充当“模板命名空间”。

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

将先前加载的模板渲染为字符串,使用“context”(如果提供)查找模板渲染期间访问的变量(例如要显示的字符串)。

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

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

返回

细绳

引擎公开了另一种在某些情况下可能有用的方法(例如,如果您需要一个单独的模板命名空间,在 Odoo Web 中,看板视图将获得自己的 QWeb2.Engine() 实例,这样它们的模板就不会与更通用的“模块”模板发生冲突):

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

在 QWeb 实例中加载模板文件(模板的集合)。模板可以指定为:

XML 字符串

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

一个网址

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

一个``Document`` or Node

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

A 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`. If present, called before compiling each DOM node to template code. In Odoo Web, this is used to automatically translate text content and some attributes in templates. Defaults to ``null`”。

1

它与 Genshi 类似,尽管它不使用(也不支持)`XML namespaces`_

2

尽管它使用了其他一些,要么是出于历史原因,要么是因为它们仍然更适合用例。 Odoo 9.0 仍然依赖于 JinjaMako