QWeb
QWeb是一个基于XML的模板引擎,可用于生成HTML片段和页面。
模板的命令是写在XML标签内以
t-
开头的属性。
一 客户端QWeb
-
QWeb解析器可用于查找模板中的特殊指令,并且动态地生成HTML以替换这些指令。
-
QWeb指令需要根据当前记录值的表达式结果进行不同的处理,在实现上有两种方式
-
客户端JavaScript(使用JavaScript语法)
-
看板视图
-
1 从模板中获取要呈现的XML 2 调用服务器端read()方法为模板中的字段取数 3 定位kanban-box模板,并且使用QWeb引擎将其转换成HTML 4 将HTML推向浏览器进行展示
-
-
服务端Python(Python语法)
- 报表/页面
-
1 JavaScript表达式
-
QWeb很多指令都是以表达式的方式来使用的
-
表达式的结果存储于上下文中
1.1 标示
-
record对象:当前的记录 raw_value:存储服务器端read()方法返回的值 value:根据用户设置进行格式化,日期、浮动、货币字段以及关系字段通常使用此值 widget:当前KanbanRecord()组件对象的引用 istance:网页客户端实例的引用
-
record:widget.record的缩写 read_only_mode:widget.read_only_mode的缩写,用于指示当前视图是否处于只读模式
1.2 转义字符:
由于QWeb的表达式要在xml中编写,所以表达式中的代码要符合XML的标准
-
lt:小于 lte:小于等于 gt:大于 gte:大于等于 ... ... ...
-
上述列出的转义字符是Odoo系统中使用的,并非XML的标准
1.3 JavaScript函数
1 kanban_color():用于将颜色索引号映射到CSS类颜色名称中
2 kanban_image(头像图标的模型,具体字段,记录的ID) # 返回值是一个URL
2 动态替换属性
在t-attf
或t-att
后连接属性并通过在属性的value中添加QWeb表达式来动态替换属性,表达式中科院使用标示去引用记录的值,可以使用{{表达式(标示)}}
和#{表达式(标示)}
去实现效果。
t-attf
和t-att
指令
2.1 示例
2.1.1根据截止日期来动态变换颜色
-
<li t-attf-class="oe_kanban_text_{{ record.deadline.raw_value and record.deadline.raw_value lt (new Date()) ? 'red':'black'}}"> <field name="date_deadline"/> </li>
-
oe_kanban_text_
后面的字符是通过一个表达式来动态书写,2-4行组成了这个表达式 -
上述class类相当于:
t-attf-class='red'
ort-attf-class='black'
-
class类中通过
{{ }}
语法,并在其中使用标示去引用值,并在表达式中使用了java的三元表达式 -
java三元表达式与python三元表达式(三目运算符)
-
java: a>b ? 'x':'y' # 如果a>b为true,则结果为x,否则为y python: x if a>b else y # 如果a>b,则结果为x,否则为y
-
2.1.2 动态方式显示title
当用户在前端页面上将鼠标悬停在用户头像上时,这种方式能提示用户的名字
t-att-title="record.user_id.value"
2.1.3 动态改变src属性
t-att-src="kanban_image('res.users','image_small','record.user_id.raw_value')"
# kanban_image()函数相当于返回了一个URL,然后再通过URL调用模型、字段、记录ID三个参数的控制器来获取图像
2.1.4 通过字典/队列的形式动态的赋值
<p t-att="{'class':'oe_bold','name':'test1'}"/>
等价于
<p class="oe_bold" name="test1"/>
class使用队列方式赋值:
<p t-att="['class','oe_bold']"/>
3 循环指令
t-foreach
为QWeb内的循环指令
3.1 示例
<t t-foreach="record.follower_ids.raw_value.slice(0,3)" t-as="rec">
<t t-esc="rec">
</t>
t-foreach
使用的时候需要t-as
配合使用
t-as
:被赋予每一个项目的引用t-esc
:接受t-as
获取引用的项目slice(0,3)
:若不想全部显示则使用切片函数
3.2 辅助变量
在循环指令中,类似t-as
是辅助t-foreach
的其中一种变量,以下还是其他辅助变量可以使用
<t t-foreach="xxx" t-as="rec">....</t>
,其中rec为每一个项目的引用
rec_index:迭代标示,代表当前项目所处的位置,从0开始
rec_size:集合的元素数
rec_first:只有当该项目是迭代的第一个项目时返回True
rec_last:当该项目时迭代的最后一个项目时返回True
rec_even:判断是否为偶数索引,如果是则返回True
rec_odd:判断是否为奇数索引,如果是则返回True
rec_parity:判断索引的奇偶索引,根据当前记录的索引来判断
rec_all:表示要迭代的对象
rec_value:该变量与rec分别保存一个字典的关键字和值,rec保存关键字,该变量保存值
4 条件指令
t-if
4.1 示例
值=0则显示无进度,大于等于1显示已完成,否则显示进度
<t t-if="record.progress.raw_value == 0">
<li>无进度</li>
</t>
<t t-elif="record.progress.raw_value get 1">
<li>已完成</li>
</t>
<t t-else="">
<li>进度<field name="progress"/></li>
</t>
5 渲染值
可以使用<field>
元素来渲染字段内容,也可以使用t-esc
指令根据表达式来转义成HTML代码
<t t-esc="record.follower_ids.raw_value" />
如果源数据是绝对安全的情况下,可以使用t-raw
指令来显示字段的数据库存储值,而不进行任何转义
<t t-raw="record.follower_ids.raw_value" />
**注意:**尽可能的少使用t-raw
,前端的数据最好还是转义程HTML在展示,这样做会更安全。
6 设置变量值
如果想在一个模板中经常使用某个变量值,可以使用
t-set
指令来完成,t-set
设置变量的名称,t-value
设置变量的值。
6.1 示例
根据截止日期来调整显示的颜色
<template>
<t t-set="red_or_black"
t-value="
record.deadline.raw_value and
record.deadline.raw_value lte (new Data())
? 'oe_kanban_text_red':'oe_kanban_text_black'"
/>
<li class="red_or_black">
<field name="deadline"/>
</li>
</template>
其中上面的t-value
的内容也可以是HTML,不一定要是表达式。
7 复用模板
QWeb模板既可以是可重复使用的HTML片段,也可以插入到其他模板中。
复用模板可以使用**
t-name
指令来定义模板的名字**,在调用模块内使用**t-call="module.name"
来调用**复用模板。
注意:
-
被调用模块内的(模板)变量和调用模块内的(模板)变量将保存在同一上下文中
-
在被调用模块内可以显式地将变量传递给调用模块
7.1示例
下面代码将使用到变量传递功能以及在调用模块内重新对变量进行赋值操作
<template>
<t t-name="follower_avatars">
<div>
<t t-foreach="record.follower_ids.raw_value.slice(0, arg_max)" t-as=rec"">
<img t-att-src="kanban_image('res.partner','image_small',rec)" class="oe_avatar" width="24" height="24"/>
</t>
</div>
</t>
</template>
<!-- 在同一模块中引用上述模板,若在其他模块则通过module.name来调用 -->
<t t-call="follower_avatars">
<t t-set="arg_max" t-value="3"/>
</t>
<!-- 在调用模块内对复用模板的arg_max进行赋值为3-->
7.2 对复用模板进行修改
使用inherit_id
<template id='xx' name='xx' inherit_id='要修改的模板'>
<xpath xxx>
<link xxx/>
<script xxx></script>
</xpath>
</template>
8 CSS和JavaScript
看板视图主要是HTML并且大量使用CSS类。
可以添加自己的CSS,后端的Odoo资产将在
assets_backend
模板中声明,若要添加模块资产,name应该扩展该模板。(用于此的XML文件通常放在views/module子目录中)继承web.assets_backend,并在里面添加上自己的CSS类和js文件路径。
8.1 示例
使用自定义CSS和JavaScript使看板视图起到渲染作用
<odoo>
<template id='xx' inherit_id="web.assets_backend" name="xxx">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/xx/xx.css"/>
<script type="text/javascript" src="/xx/xx.js">
</script>
</xpath>
</template>
</odoo>
二 报表
1 安装wkhtmltopdf
要正确生成报表,需要安装wkhtmltopdf实用程序的推荐版本,Odoo会使用wkhtmltopdf来将呈现的HTML页面转换为PDF文档
- 已知某些版本的wkhtmltopdf库存在问题,建议使用0.12.1
- 使用
wkhtmltopdf --version
命令查看版本信息
2 创建商业报表
报表文件应放在/report
子目录下
2.1 示例
<odoo>
<data>
<report id="action_xxx_report"
string="xxx"
model="xx.xx"
report_type="qweb-pdf"
name="module_name.identifier_name"
/>
</data>
</odoo>
其中identifier_name
是template模板的id名
三 服务器QWeb
1 QWeb报表模板
由于报表知识QWeb模板,因此可以应用继承。
报表中使用QWeb模板可以使用XPATH表达式的常规继承视图进行扩展。
1.1 列表报告格式
列表报告格式:其中每条记录都是报告中的一行。
报表标题区域通常用于显示标题,报表页脚区域通常用于显示总计。
<template id="report_xxx_tempalte">
<t t-call="web.html_container">
<t t-call="web.external_layout">
<div class="page">
<!-- 报表头部信息 -->
<t t-foreach="docs" t-as="o">
<!-- 报表行内容 -->
</t>
<!-- 报表底部内容 -->
</div>
</t>
</t>
</template>
web.html_container
模板可执行基本设置以支持HTML文档。
web.external_layout
模板一般使用相应公司的相应设置处理报表页眉和页脚。
web.internal_layout
模板用于基本表头。
1.2 文档报表格式
文档报表格式:每条记录都是一个页面,如邮寄信件。
<template id="report_xxx_template">
<t t-call="web.html_container">
<t t-call="web.external_layout">
<t t-foreach="docs" t-as="o">
<div class="page">
<!-- 报表内容 -->
</div>
</t>
</t>
</t>
</template>
2 报表展示数据
与看板视图不同的是,报表中的QWeb模板将在服务器端呈现并使用Python QWeb实现。
报表中的QWeb模板使用的是Python语法,而不是JavaScript表编写QWeb表达式。
2.1 变量
docs:是一个可以迭代的集合,其中包含了要打印的记录。
doc_ids:表示要打印的记录的ID列表。
doc_model:表示模型的标志。
time:是对Python时间库的引用。
user:是允许报表的用户的记录。
res_company:表示当前用户的公司记录
可以使用t-field
属性引用字段值,并且可以使用t-options
属性进行补充,以使用特定的窗口小部件来呈现字段内容。
3 基于客制化SQL的报表
前面介绍的报告都是基于常规记录集的。在某些情况下,我们需要在QWeb模板中以更加个性化的处理方式转换或聚合数据。
有两种方法:编写SQL查询和向导
3.1 编写SQL查询
编写SQL查询来构建我们需要的数据集,通过特殊模型公开这些结果,并使我们的报告基于查询的记录集进行工作。
3.1.1 定义模型
前提:原本已经有一个bug模型,现在为bug模型添加基于客制化SQL的报表
report/bug_report.py
from odoo import models,fields
class BugReport(models.Model):
_name = "bug.report"
_description = 'Bug Report'
_auto = False # 禁用数据库表的自动创建,在init()中提供替代SQL
name = fields.Char('描述')
is_done = fields.Boolean('是否完成')
active = fields.Boolean('是否有效')
user_id = fields.Many2one('res.users','负责人')
date_deadline = fields.Date('截止日期')
def init(self):
self.env.cr.execute("""
CREATE OR REPLACE VIEW bug_report AS
(SELECT * FROM bm_bug WHERE active=True)
""")
# 创建一个数据库视图,其中bm_bug是已经定义好的模型/数据库的表
auto
属性可用于禁用数据库表的自动创建,在模型的init()方法中为它提供了替代SQL。
上述语句会创建一个数据库视图,用于提供报告所需的数据。
3.1.2 为模型添加新报告
值得注意的是,上述定义的bug.report模型是一个新的、不同的模型,并且与bug模型没有相同的记录访问规则。
通常,访问报告的用户将能够看到所有的bug。
为了让他们只能看到自己的bug,我们需要将添加到bug模型的记录规则添加到报表模型中。
接下来,我们可以基于此模型添加新报告。
report/bug_report_view.xml
<odoo>
<report id="action_bug_model_report"
string="To-do Special Report"
model="todo.task"
report_type="qweb-html"
name="bug_adv.report_bug_special"/>
<template id="action_bug_model_report">
<t t-call="web.html_container">
<t t-call="web.external_layout">
<div class="page">
<!-- 报表页面内容 -->
<table class="table table-striped">
<tr>
<th>Title</th>
<th>Owner</th>
<th>Deadline</th>
</tr>
<t t-foreach="docs" t-as="o">
<tr>
<td class="col-xs-6">
<span t-field="o.name"/>
</td>
<td class="col-xs-3">
<span t-field="o.user_id"/>
</td>
<td class="col-xs-3">
<span t-field="o.date_deadline" t-options="{'widget':'date'}"/>
</td>
</tr>
</t>
</table>
</div>
</t>
</t>
</template>
</odoo>
助于理解:
div:division,分隔
span:范围
th:table header cell,表头单元格
tr:table row,表格中的一行
td:table data cell,单元格
3.2 向导
对于更复杂的情况,可以使用向导。
使用向导的情况下,需要创建一个瞬态模型来保存用户引入的报告参数。
四 总结
1 了解了如何使用QWeb设计看板视图。
2 了解了QWeb报告引擎。
3 了解了使用QWeb模板语言构建报告时最重要的技术。