本文为最好用的免费ERP系统Odoo 12开发手册系列文章第十篇。
本文将学习如何为用户创建图形化界面来与图书应用交互。我们将了解不同视图类型和小组件(widgets)之间的差别,以及如何使用它们来提供更优的用户体验。
本文主要内容有:
- 菜单项
- 窗口操作(Window Actions)
- 表单视图结构
- 字段
- 按钮和智能按钮
- 动态视图元素
- 列表视图
- 搜索视图
- 其它视图类型
开发准备
我们将继续使用library_checkout插件模块,它已经有了模型层,现在需要视图层来实现用户界面。本文中的代码基于第八章 Odoo 12开发之业务逻辑 – 业务流程的支持,相关代码请参见 GitHub 仓库,本章完成后代码也请参见 GitHub仓库。
菜单项
用户界面的入口是菜单项,菜单项形成一个层级结构,最顶级项为应用,其下一级为每个应用的主菜单。还可以添加更深的子菜单。可操作菜单与窗口操作关联,它告诉客户端在点击了菜单项后应执行什么操作。
菜单项存储在ir.ui.menu模型中,可通过Settings > Technical > User Interface > Menu Items菜单进行查看。
library_app模块为图书创建了一个顶级菜单,library_checkout插件模块添加了借阅和借阅阶段的菜单项。在library_checkout/views/library_menu.xml文件中,借阅的菜单项 XML 代码如下:
1 2 3 4 |
<menuitem id="menu_library_checkout" name="Checkout" action="action_library_checkout" parent="library_app.menu_library" /> |
这里有一个<menuitem>快捷元素,提供了一种定义菜单项的简写方式,比原生的<record model=”ir.ui.view”>元素要更为便捷。以上使用的属性有:
- name是展示在用户界面中的菜单项标题
- action是点击菜单项时运行的窗口操作的XML ID
- parent是父级菜单项的XML ID。本例中父级项由其它模块创建,因此们使用了完整的XML ID, <module>.<XML ID>进行引用。
还有以下可用属性:
- sequence设置一个数字来在展示菜单项时进行排序,如sequence=”10″
- groups是一个逗号分隔的可访问菜单项安全组的XML ID列表,如groups=”library_app.library_group_user, library_app.library_group_manager”
- web_icon是菜单项的图标,仅用于企业版的顶级菜单项,如web_icon=”library_app,static/description/icon.png”
窗口操作(Window Actions)
窗口操作给 GUI(图形化用户界面)客户端操作指令,通常用于菜单项或视图中的按钮。它告诉 GUI 所作用的模型以及要显示的视图。这些操作可以通过域过滤器过滤出可用记录,设置默认值以及从上下文属性中过滤。窗口操作存储在ir.actions.act_window模型中,可通过Settings > Technical > Actions > Window Actions菜单进行查看。
在library_checkout/views/library_menu.xml文件中,我们可以找到借阅菜单项中使用的窗口操作,我们需要对其进行修改来启用本文中将添加的视图类型:
1 2 3 4 |
<act_window id="action_library_checkout" name="Checkouts" res_model="library.checkout" view_mode="tree,form,activity,calendar,graph,pivot" /> |
窗口操作通常像以上这样使用<act_window>快捷标签创建。这里修改”tree, form”为更大的列表”tree, form, activity, calendar, graph, pivot”。以上使用的窗口操作属性有:
- name是通过操作打开的视图中显示的标题
- res_model是目标模型的标识符
- view_mode是一个逗号分隔的可用视图类型列表。第一项为默认打开时的视图。
窗口操作还有一些其它属性:
- target:如果设置为 new,会在弹出的对话框窗口中打开视图,例如target=”new”。默认值是current,在主内容区行内打开视图。
- context:为目标视图设置上下文信息,可设置默认值或启用过滤器等,例如context=”{‘default_user_id’: uid}”。
- domain:是对可在打开视图中浏览的记录强制过滤的域表达式,例如domain=”[(‘user_id’, ‘=’, uid)]”。
- limit:列表视图中每页显示的记录数,例如limit=”80″。
做了这些修改后,在选择Checkouts菜单项并浏览相应的列表视图时,右上角在列表和表单按钮后会增加一些按钮。但在我们创建对应视图前并不能使用,本文将一一学习。窗口操作还可在列表和表单视图的上方的 Action 菜单按钮中使用,它在 Fitlers 按钮旁。要使用这个,我们需要在元素中添加以下两个属性:
- src_model设置Action所作用的模型,例如src_model=”library.checkout”
- multi=”true”也启用列表视图中的Action,这样它可以作用于多个已选记录。否则仅在表单视图中可用,并且一次只能应用于一条记录。
补充:此时打开借阅表单会提示Insufficient fields for Calendar View!,在编写日历视图前最好选视图模式里删除 calendar 来进行效果查看
表单视图结构
表单视图要么按照简单布局,要么按与纸质文档相似的业务文档布局。我们将学习如何设计这些业务文档布局以及使用可用的元素和组件。要进行这一学习,我们重新查看并扩展第八章 Odoo 12开发之业务逻辑 – 业务流程的支持中创建的图书借阅表单。
业务文档视图
业务应用中记录的很多数据可以按纸质文档那样展示。表单视图可模仿这些纸质文档来提供更直观的用户界面。例如,在我们的应用中,可以把一次借阅看作填写一张纸,我们将编写一个遵循这一设计的表单视图。编辑library_checkout/views/chceckout_view.xml文件并修改表单视图记录来带有业务文档视图的基本框架:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<record id="view_form_checkout" model="ir.ui.view"> <field name="model">library.checkout</field> <field name="arch" type="xml"> <form> <header> <!--以下仅供查看效果使用--> <field name="state" widget="statusbar" clickable="True" /> </header> <sheet> ... </sheet> <div class="oe_chatter"> <field name="message_follower_ids" widget="mail_followers" /> <field name="activity_ids" widget="mail_activity" /> <field name="message_ids" widget="mail_thread" /> </div> </form> </field> </record> |
视图名称是可选的,在不写时会自动生成。为简便以上利用了这一点,在视图记录中省略了<field name=”name”>元素。可以看到业务文件视图通常使用三大区域:
- header状态栏
- sheet主内容
- 底部交流区,也称作chatter
底部的交流区使用了 mail 插件模块中提供的社交网络组件。可使用这些,我们的模型需要继承mail.thread和mail.activity.mixin,可参见第八章 Odoo 12开发之业务逻辑 – 业务流程的支持。
头部 Header
头部header 通常用于文档所走过的生命周期或步骤,还包含相关的操作按钮。这些按钮是普通表单按钮,最重要的下一步可以高亮显示。
头部按钮
编辑表单视图中的<header>版块,我们添加一个按钮来更易于设置归还的借阅为完成(done):
1 2 3 4 5 6 7 8 |
<header> <field name="state" invisible="True" /> <button name="button_done" string="Return Books" attrs="{'invisible': [('state', 'in', ['new', 'done'])]}" class="oe_highlight" /> </header> |
这里我们在头部添加了一个Return Books 按钮,在点击时调用button_done模型方法。注意可使用class=”oe_highlight”来对用户高亮显示操作。例如,在有几个可选按钮时,我们可以高亮显示主操作或下一步要执行的“更多”操作。attrs用于在 New 和 Done 状态时隐藏该按钮。实现这点的条件使用了不会在表单显示的 state 字段。要使条件生效,我们需要将使用的所有值在网页客户端中加载。我们不打算向终端用户显示 state 字段,因此使用 invisible 将其添加为不可见字段。
ℹ️domain 或 attrs 表达式中使用的字段必须在视图中加载,作用于它们的<field>元素。如果字段不对用户可见,则必须以不可见字段元素对其进行加载。
本例中我们使用的是 state 字段,相同的效果可通过 states 字段属性实现。虽然没有 attrs 属性灵活,但它更为精简。可将 attrs 一段替换为如下代码:
1 2 3 4 5 |
<button name="button_done" type="object" string="Returned" states="open,cancel" class="oe_highlight" /> |
attrs和states元素可见功能也可用于其它视图元素,如 field。本文后续会深入讨论。要让按钮可以运作,我们还需要实现调用的方法。在library_checkout/models/library_checkout.py file文件的借阅类里添加以下方法:
1 2 3 4 5 6 7 8 |
def button_done(self): Stage = self.env['library.checkout.stage'] done_stage = Stage.search( [('state', '=', 'done')], limit=1) for checkout in self: checkout.stage_id = done_stage return True |
该方法首先查找 done 阶段的记录来使用,然后对 self 记录集中的每条记录,设置其 stage_id 值为完成阶段。
阶段管道
下面我们为头部添加状态条组件,显示文档所在阶段。从代码层面说,是使用statusbar组件的stage_id字段的<field>元素:
1 2 3 4 5 6 7 |
<header> ... <field name="stage_id" widget="statusbar" clickable="True" options="{'fold_field': 'fold'}" /> </header> |
这会在头部添加一个阶段管道组件,它在表示文档当前所在生命周期点的字段上使用了statusbar组件。通常是一个状态选项字段或阶段many-to-one字段。这两类字段在 Odoo 核心模块中多次出现。clickable属性让用户可通过点击状态条来修改文档阶段。一般需要开启它,但有时又不需要,比如需要对工作进行更强的控制,并且要求用户仅使用可用的操作按钮来进入下一步。这种方法允许在切换阶段时进行指定验证。
对阶段使用状态条组件时,我们可将很少使用的阶段隐藏(折叠)在 More 阶段组中。对应的阶段模型必须要有一个标记来配置需隐藏的阶段,通常命名为 fold。然后statusbar组件使用 options 属性来将这一字段名提供给fold_field选项,如以上代码所示。
使用状态代替阶段
阶段是一个使用了模型来设置进度步骤的many-to-one字段。因此终端用户可对其动态配置来符合他们具体的业务流程以及支持看板的完美展示。我们将在图书借阅中使用到state。
状态是一个包含了流程中相当稳定步骤的选择列表,如新建、处理中和完成。终端用户无法对其进行配置,因为它是静态的,更易于在业务逻辑中使用。视图字段对状态甚至还有特别的支持:状态字段属性仅在记录处理特定状态才对用户开放。
ℹ️阶段引入的时间要晚于状态。两者现在共存,在 Odoo 内核的趋势是使用阶段来替代状态。但如前所述,状态仍提供一些阶段所不具备的功能。
可通过将阶段映射到状态中来同时获得两者的优势。在借阅模型中我们通过向借阅阶段中添加一个状态字段来实现,借阅文档通过一个关联字段来使用状态。使用状态代替阶段的模型中,我们也可以使用进度条管道。这种情况下要在进度条中列出状态,需要使用statusbar_visible属性来替换fold_field选项。具体代码如下:
1 2 3 4 |
<field name="state" widget="statusbar" clickable="True" statusbar_visible="draft,open,done" /> |
注意在我们实际的图书借阅项目中并不能这么使用,因为它是阶段驱动的,而非状态驱动。
文档表单
表单画布是表单的主区域,这里放置实际的数据元素,设计上类似一张真实的纸质文档,通常 Odoo 中的这些记录也会被称为文档。通常文档表单结构包含如下区域:
- 左上角文档标题和副标题
- 右上角按钮区
- 其它文档头部字段
- 底部笔记区,将附加字段组织成选项卡或页面
文档各行通常在笔记区的第一页,在表单之后,通常有一个 chatter 组件,带有文档订阅者、讨论消息和活动规划。下面逐一了解这些区域。
补充:关于sheet的翻译Alan的理解sheet 仅为单(据),但出于行文习惯一律使用表单
标题和副标题
一个元素之外的字段不会自动带有渲染它们的标签。对于标题元素就是如此,因此该元素应用来对其进行渲染。虽然要花费额外的工作量,但这样的好处是对标签显示控制有更好的灵活性。常规 HTML,包括 CSS 样式元素,可用于美化标题。一般标题放在oe_title类中。以下为扩展后的<sheet>元素,它包含标题以及一些额外字段如副标题:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<sheet> <field name="member_image" widget="image" class="oe_avatar" /> <div class="oe_title"> <label for="member_id" class="oe_edit_only" /> <h1><field name="member_id" /></h1> <h3> <span class="oe_read_only">By </span> <label for="user_id" class="oe_edit_only" /> <field name="user_id" class="oe_inline" /> </h3> </div> <!-- More elements will be added from here... --> </sheet> |
此处可以看到我们使用了div, span, h1和h3这些常规 HTML 元素。<label>元素让我们可以控制字段标签在何时何处显示。for 属性标识了获取标签文件的字段。也可以使用 string 属性来为标签提供具体的文本。本例中还使用了
我们还可在表单左上角标题旁包含展示图像。它用在 parnter 或产品这类模型的表单视图中。作为示例,我们在标题区前添加了一个member_image字段,它使用图像组件widget=”image”,以及特定的 CSS 类class=”oe_avatar”。该字段尚未添加至模型中,下面我们就来添加,我们使用关联字段来将会员的图片显示在借阅文档中。编辑library_checkout/models/library_checkout.py文件并在借阅类中添加如下字段:
1 |
member_image = fields.Binary(related='member_id.partner_id.image') |
表单内容分组
表单主内容区应通过<group>标签来进行组织。<group>标签在画布中插入了两列。默认在这些列中标签会在字段旁显示,因此又占据两列。字段加标签会占据 一行,下一个字段和标签又会另起一行,垂直排列。Odoo表单的常见布局是带标签的字段并排成两列。达到这一效果,我们只需要添加两个嵌入顶部的<group>标签。
继续修改表单视图,在主内容区标题<div>后添加如下代码:
1 2 3 4 5 6 7 8 9 10 |
<group name="group_top"> <group name="group_col1"> <field name="user_id" /> <field name="checkout_date" /> </group> <group name="group_col2"> <field name="state" /> <field name="closed_date" /> </group> </group> |
为 group 标签分配name是一个好的编码实践,这样在其它模块中继承时会更易于对它们进行引用。还可设置 string 属性,一旦设置将作为该部分的标题来显示。
ℹ️Odoo 11中的修改
string 属性不能作为继承的锚点,因为在应用继承前会对其进行翻译。这时应使用 name 属性来代替它。
在 group 内,<newline>元素会强制在新的一行,下一个元素会渲染到组的第一列。附加的版块标题可通过组内<separator>元素添加,如果带有 string 属性也会显示标题标签。要更好地控制元素布局,我们可以使用col和colspan属性。
col 属性可用于<group>元素中来自定义包含的列数。如前所述,默认为两列,但可修改为任意其它数字。双数效果更佳,因为默认每个添加的字段会占据两列:字段标签和字段值。按照以下代码我们通过colspan=”2″ 来在一个组内将4个字段放在两列中显示:
1 2 3 4 5 6 7 8 9 10 11 12 |
<group name="group_top"> <group name="group_col1" col="4" colspan="2" string="Group 1"> <field name="user_id" /> <field name="state" /> <field name="checkout_date" /> <field name="closed_date" /> </group> <group name="group_col2" string="Group2" /> </group> |
以上我们使用 string 属性为组添加了标题,来更清楚地看组所在位置。注意字段的顺序不同,它们先是从左到右,然后从上到下。<group>元素可以使用 colspan 属性来设置它所占用的具体列数。默认和带标签的字段一样为两列。可以修改以上代码中 col 和 colspan 的值来在表单中查看不同的效果。比如 col=”6″ colspan=”4″的效果是什么样的?可以试一试(见下图)。
选项卡笔记本(Tabbed notebooks)
另一种组织内容的方式是 notebook 元素,一个包含多个称为页面(page)的选项卡分区的容器。它们可以让不常用的内容在不使用时隐藏起来,或者用于按话题组织大量字段。
我们将在借阅表单中添加一个带有已借图书列表的notebook 元素。在前面的<group name=”group_top”>元素后可添加如下代码:
1 2 3 4 5 |
<notebook> <page string="Borrowed Books" name="page_lines"> <field name="line_ids" /> </page> </notebook> |
本例中笔记本仅有一个页面。添加更多,我们需在<section>元素内添加更多的<page>版块。页面画布默认不会渲染字段标签,如需显示,需像表单主画布那样将字段放在<group>版块内。本例中我们在页面中添加了one-to-many字段line_ids,我们已经有了页面标题,因此不需要标签。page支持以下属性:
- string:选项卡的标题(必填)
- attrs:不可见属性与表达式映射的字典
- accesskey:HTML访问密钥
字段
视图字段有一些可用属性。大部分从模型定义中获取值,但可在视图中覆盖。以下来快速查看字段的可用属性:
- name标识字段数据库中名称
- string用于想要覆盖模型中标签文本的标签文本
- help是鼠标悬停在字段上显示的提示文本,它允许我们覆盖模型定义中提供的帮助文本
- placeholder是在字段中显示的提示文本
- widget让我们可以覆盖字段的默认组件,一会儿我们就会讲到可用的组件
- options是一个带有组件附加数据的JSON数据结构,值随各组件的不同支持而不同
- class是用于字段 HTML 渲染的CSS类
- nolabel=”True”阻止自动字段标签的展示。仅对<group>元素内的字段有作用,通常与<label for=”…”>元素一起使用。
- invisible=”True”让字段不可见,但仍会从服务端获取数据并可在表单中使用
- readonly=”True”让表单中该字段不可编辑
- required=”True”让表单中该字段为必填
一些特定字段的属性如下:
- password=”True”用于文本字段。显示为密码项,隐藏所输入文字
- filename用于二进制字段,它是用于存储上传文件名的模型字段的名称
字段标签
<label>元素可用于更好地控制字段标签的展示。一个使用示例是仅在表单为编辑模式时展示标签:
1 |
<label for="name" class="oe_edit_only" /> |
这么做时,如果字段在<group>元素内部,我们通常还要对其设置nolabel=”True”。class=”oe_edit_only”可用于应用 CSS 样式,让标签仅在编辑模式下可见。
字段组件
每个字段类型都会使用相应的默认组件在表单中显示。但还有一些替代组件可以使用。对于文本字段,有如下组件:
- email用于让 email 文本成为可操作的”mail-to”地址
- url用于将文本格式化为可点击的URL
- html用于将文本渲染为HTML内容;在编辑模式下,它显示为一个WYSIWYG(所见即所得)编辑器,可在不使用 HTML 代码的情况下格式化内容。
对于数字字段,有以下组件:
- handle在列表视图中作为一个排序字段,显示一个句柄来让我们可以拖放进行自定义排序
- float_time将一个浮点型字段格式化为带有小时和分钟的值
- monetary将一个浮点型字段显示为货币金额。它与currency_id字段一起使用,还可以通过options=”{‘currency_field’: ‘currency_id’}”来使用另一个字段名
- progressbar将一个浮点值显示为进度条百分比,有助于将字段展示为完成率
- percentage和percentpie组件可用于浮点型字段
对于关联和选择项字段,有以下附加组件:
- many2many_tags将值显示为按钮标签列表
- many2many_checkboxes将选项值显示为一个复选框列表
- selection对many-to-one字段使用选择字段组件
- radio以单选按钮显示选择字段选项
- priority将选项字段显示为一个可点击星形列表。选择项目通常是数值。
- state_selection将看板状态选择列表显示为信号灯。普通状态显示为灰色,完成显示为绿色,其它状态显示为红色。
- pdf_viewer是一个二进制字段(在 Odoo 12中引入)。
ℹ️Odoo 11中的修改
state_selection在 Odoo11中引入来替换掉kanban_state_selection。后者被淘汰,但为保持向后兼容性,还支持使用。
关联字段
在关联字段中,我们可让用户操作做一些额外控制。默认用户从这些字段中创建新记录(也称作“快速创建”)并打开关联记录表单。可通过options字段属性来关闭:
1 |
options="{'no_open': True, 'no_create': True}" |
context和domain也是字段属性并对于关联字段特别有用。context可定义关联字记录默认值,domain 可限制可选记录。常见的示例为让一个字段依赖其它字段值来产生选择项。domain可在模型中直接定义,但也可在视图中进行覆盖。
在to-many字段中,我们还可使用 mode 属性来更改用于显示记录的视图类型。默认为 tree,但还有其它选项:form, kanban或graph。关联字段可定义行内指定视图来使用。这些视图在元素中的嵌套视图定义中声明。例如,在line_ids借阅中,我们可以为这些线路定义特定的列表和表单视图:
1 2 3 4 5 6 7 8 9 10 11 12 |
<notebook> <page string="Borrowed Books" name="page_lines"> <field name="line_ids"> <tree> <field name="book_id" /> </tree> <!--form> <field name="book_id" /> </form--> </field> </page> </notebook> |
线路列表将带有给定的<tree>定义。当我们与线路交互时,弹出一个表单对话框,在<form>定义中包含该结构。
小贴士:如果想要在列表视图的表单弹出窗口中直接编辑one-to-many路线,应使用<tree editable=”top”>或<tree editable=”bottom”>
按钮
按钮支持这些属性:
- string是按钮文本标签或使用图标时的 HTML alt 文本
- type是执行操作的类型,有以下值:
- object用于调用 Python 方法
- action用于运行窗口操作
- name标识按所选类型要操作的具体的操作,要么是模型方法名,要么是要运行的窗口操作的数据库 ID。可使用%(xmlid)d方程式来将XML ID转换成加载视图时所需的数据库 ID。
- args在类型为 object 时用于向方法传递额外的参数,须是在形成方法调用参数的记录 ID 之后所添加的纯静态 JSON 参数。
- context在上下文中添加值,可在窗口操作或 Python 代码方法调用之后产生效果。
- confirm在运行相关操作之前显示确认消息框,显示的内容是属性中分配的文本。special=”cancel”用于向导表单。
- icon是按钮所显示的图标。可用的按钮来自Font Awesome图标集,版本为4.7.0,应通过对应的 CSS 类来指定,如icon=”fa-question”。更多信息可访问Font Awesome。
ℹ️Odoo 11中的修改
在 Odoo 11之前,按钮图标是来自GTK客户端库的图片,并且仅限于addons/web/static/src/img/icons中所保存图片。ℹ️Odoo 11中的修改
在 Odoo 11中工作流引擎被淘汰并删除。此前的版本中,在支持工作流的地方,按钮可通过type=”workflow”来触发工作流引擎信号。这时name属性用于工作流的信号名。
智能按钮
在右上角版块中带有智能按钮(smart button)也很常见。智能按钮显示为带有数据指示的矩形,在点击时可进入。
Odoo 中使用的 UI样式是在放置智能按钮的地方带有一个隐藏框,按钮框通常是<sheet>的第一个元素,在<div class=”oe_title”>元素前(以及头像),类似这样:
1 2 3 |
<div name="button_box" class="oe_button_box"> <!-- Smart buttons will go here... --> </div> |
按钮的容器是一个带有oe_button_box类的 div 元素。在 Odoo 11.0以前,可能需要添加一个oe_right类来确保按钮框在表单中右对齐。在我们的应用中,我们将在按钮中显示图书会员待归还的其它借阅的总数,点击按钮会进入这些项的列表中。
所以我们需要该会员处于 open 状态的借阅记录,排除掉当前借阅。对于按钮统计,我们应创建一个计算字段来在library_checkout/models/library_checkout.py文件的借阅类中进行计数:
1 2 3 4 5 6 7 8 9 10 |
num_other_checkouts = fields.Integer( compute='_compute_num_other_checkouts') def _compute_num_other_checkouts(self): for rec in self: domain = [ ('member_id', '=', rec.member_id.id), ('state', 'in', ['open']), ('id', '!=', rec.id)] rec.num_other_checkouts = self.search_count(domain) |
下一步我们可以添加按钮框并在其中添加按钮。在<sheet>版块的上方,替换上面的按钮框占位符为以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 |
<div name="button_box" class="oe_button_box"> <button class="oe_stat_button" icon="fa-tasks" help="Other checkouts pending return." type="action" name="%(action_other_checkouts_button)d" context="{'default_member_id': member_id}"> <field string="To Return" name="num_other_checkouts" widget="statinfo" /> </button> </div> |
按钮元素本身是一个带有显示数据字段的容器。这些数据是使用statinfo特定组件的普通字段。该字段通常是作用于模型中定义的计算字段。除字段外,在按钮中还可以使用静态文本,如<div>Other Checkouts</div>。其它待借阅的数量展示在按钮定义中的num_other借阅字段中。
智能按钮必须带有class=”oe_stat_button” CSS样式,并应使用 icon 属性来带有一个图标。它有一个type=”action”,表示点击按钮时将运行通过 name 属性标识的窗口操作。%(action_other_checkouts_button)d表达式返回要运行的操作的数据库 ID。
在点击按钮时,我们要查看当前会员的其它借阅列表。这可通过action_other_checkouts_button窗口操作来实现。该操作会使用合适的域过滤器打开一个图书借阅列表。操作和相应的域过滤器在表单上下文之外处理,无法访问表单数据。因此按钮必须在上下文中设置当前member_id 来供窗口操作随后使用。使用的窗口操作必须在表单之前定义,因此我们应在 XML 文件根元素<odoo>中的最上方添加以下代码:
1 2 3 4 5 6 7 |
<act_window id="action_other_checkouts_button" name="Open Other Checkouts" res_model="library.checkout" view_mode="tree,form" domain="[('member_id', '=', default_member_id), ('state', 'in', ['open']), ('id', '!=', active_id)]"/> |
注意我们在域过滤器中如何使用default_member_id上下文键。该键还会点击按钮链接创建新任务时为member_id字段设置默认值。域过滤器也需要当前 ID。这无需在上下文中明确设置,因为网页客户端会在active_id上下文键中自动进行设置。
以下是可在智能按钮中添加的属性,供您参考:
- class=”oe_stat_button”渲染的不是普通按钮而是一个矩形
- icon从Font Awesome图标集中选择图标来使用。访问Font Awesome查看有哪些图标。
- type和name是按钮类型以及触发的操作名。对于智能按钮,类型通常是 action,指定窗口操作,名称为所要执行操作的 ID。应传入真实数据库 ID,因此我们要使用方程式来将XML ID转换为数据库 ID:”%(actionxmlid)d”。这一操作应该会打开带有关联记录的视图。
- string为按钮添加标签文本,这里没有使用因为所包含的字段中已经提供了文本。
- context应用于为目标视图设置默认值,用于点击按钮后视图上新建的记录。
- help在鼠标悬停在按钮上显示帮助提示信息
动态视图元素
视图元素还支持一些允许视图按字段值动态变更外观或行为的属性。我们可以有onchange 事件来在编辑表单数据时修改其它字段值,或在满足特定条件时让字段为必填或显示。
onchange 事件
onchange机制允许我在某一特定字段变更时修改其它表单字段。例如一个商品字段的 onchange可以在商品被修改时设置价格字段为默认值。在老版本中,onchange 事件在视图级别定义,但8.0之后直接在模型层中定义,无需在视图上做任何特定标记。这通过使用@api.onchange(‘field1’, ‘field2’, …) 装饰器创建模型,来对一些字段绑定 onchange 逻辑。onchange 模型方法在第八章 Odoo 12开发之业务逻辑 – 业务流程的支持中详细讨论过,其中还有相关示例。
onchange 机制还可以在用户输入时即时反馈进行计算字段的自动重算。继续使用商品来举例,如果在修改商品时价格字段变化了,它还会根据新的价格自动更新计算后的总金额字段。
动态属性
一些属性允许我们根据记录的值来动态变更视图元素的显示。指定用户界面元素的可见性可通过如下属性很方便地控制:
- groups可根据当前用户所属安全组来让元素可见。仅指定组的成员可看到该元素。它的值应为一个逗号分隔的XML ID列表
- states可根据记录的状态字段来让元素可见。它的值为一个逗号分隔的状态列表,仅对带有state 字段的模型生效。
除这些以外,我们有一些灵活的方法来根据客户端动态生成的表达式设置元素可见性。它是一个特别属性 attrs,它的值为一个映射invisible属性值与表达式结果的字典。例如,要让closed_date字段在new和open状态时不可见,可使用如下代码:
1 2 |
<field name="closed_date" attrs="{'invisible':[('state', 'in', ['new', 'open'])]}"/> |
invisible不只在字段中可用,在任意元素中均可用。例如,它可用于 notebook 页面和group元素中。attrs属性也可为其它两个属性设置值:readonly和required。它们仅对数据字段有意义,通过二者来让字段可编辑或为必填。这让我们可以实现一些基础客户端逻辑,如根据其它字段值(如 state)来让字段设为必填。
列表视图
学到这里可能不太需要介绍列表视图了,但它还一些有趣的额外属性可以讨论。下面我们修改library_checkout/views/checkout_view.xml文件来改进第八章 Odoo 12开发之业务逻辑 – 业务流程的支持中的版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<record id="view_tree_checkout" model="ir.ui.view"> <field name="name">Checkout Tree</field> <field name="model">library.checkout</field> <field name="arch" type="xml"> <tree decoration-muted="state in ['done', 'cancel']" decoration-bf="state=='open'"> <field name="state" invisible="True" /> <field name="request_date" /> <field name="member_id" /> <field name="checkout_date" /> <field name="stage_id" /> <field name="num_books" sum="# Books" /> </tree> </field> </record> |
行文本颜色和字体可根据 Python 表达式计算结果来动态变化。这通过decoration–NAME属性带上计算字段属性的表达式来实现。NAME可以是bf或it,分别表示粗体和斜体,也可以是其它Bootstrap文本上下文颜色:danger, info, muted, primary, success或warning。Bootstrap文档中有相关显示示例。
ℹ️Odoo 9中的修改
decoration-NAME 属性在 Odoo 9中引入。在 Odoo 8中使用是 colors 和 fonts 属性。
记住表达式中使用的字段必须要在<field>字段中声明,这样网页客户端才知道要从服务端获取该列。如果不想对用户显示,应对其使用invisible=”1″属性。其它 tree 元素的相关属性有:
- default_order让我们可以覆盖模型中的默认排序,它的值和模型中定义的排序格式相同。
- create, delete和edit,如果设为 false(字母小写),会禁用列表视图中的相应操作。
- editable让记录在列表视图中可直接被编辑。可用值有 top 和 bottom,表示新记录添加的位置。
列表视图可包含字段和按钮,表单中的大部分属性对它们也有效。在列表视图中,数值字段可显示为对应列的汇总值。为字段添加一个累加属性(sum, avg, min或max)会为其分配汇总值的标签文本。我们在 num_books 字段中添加了一个示例:
1 |
<field name="num_books" sum="# Books" / |
num_books字段计算每个借阅中的图书数量,它是一个计算字段,我们需要在模型进行添加:
1 2 3 4 5 6 |
num_books = fields.Integer(compute='_compute_num_books') @api.depends('line_ids') def _compute_num_books(self): for book in self: book.num_books = len(book.line_ids) |
搜索视图
可用的搜索选项通过<search>视图类型来定义。我们可以选择在搜索框中输入时自动搜索的字段。还可以预置过滤器,通过点击启用,以及在列表视图中的预置分组选项。图书借阅的搜索视图可设置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<record id="view_filter_checkout" model="ir.ui.view"> <field name="model">library.checkout</field> <field name="arch" type="xml"> <search> <field name="member_id" /> <field name="user_id" /> <filter name="filter_not_done" string="To Return" domain="[('state','=','open')]" /> <filter name="filter_my_checkouts" string="My Checkouts" domain="['user_id', '=', uid]" /> <filter name="group_user" string="By Member" context="{'group_by': 'member_id'}" /> </search> </field> </record> |
在<search>视图定义中,可以看到两个member_id和user_id的简单元素,当用户在搜索框中输入时,推荐下拉框中会显示对这些字段的匹配。然后有两个使用域过滤器的预置过滤器。可在搜索框下方的 Filter 按钮下选择。第一个过滤器是 To Return 图书,也就还处于 open 状态的图书。第二个过滤器是当前图书管理员处理的图书,通过当前用户的 user_id (可在上下文的 uid 键中获取)过滤。
这两个过滤器可以分别被启用并以 OR运算符连接。以<separator />元素分隔的整块过滤器以 AND 运算符连接。
第三个过滤器仅设置 group by 上下文键,它让视图按照字段来对记录分组,本例中为 member_id 字段。
字段元素可使用如下属性:
- name标识要使用的字段
- string用作标签文本,它会替换默认值
- operator用于修改默认的运算符(默认值:数值字段=,其它字段类型ilike)
- filter_domain设置搜索使用的特定域表达式,为 operator 属性提供一种灵活的替代方式。搜索文本在表达式中通过 self 引用。一个简单示例:filter_domain=”[(‘name’, ‘ilike’, self)]”
- groups让对该字段的搜索仅向安全组内成员开发,它的值是一个逗号分隔的XML ID列表
过滤元素有以下可用属性:
- name用作后续继承/扩展或通过窗口操作启用的标识符。这不是必填项,但包含该属性是一个不错的编码习惯。
- string是过滤器显示的标签文本,必填
- domain是加入当前域的域表达式
- context是加入当前上下文的上下文字典。通常使用group_id作为键,用于对记录分组的字段名作为值
- groups让该字段的搜索仅对安全组列表(XML IDs)成员开放
其它视图类型
表单、列表和搜索视图是最常用的视图类型。但还有一些其它的视图类型可用于设计用户界面。对于前述三种基本视图类型我们已经很熟悉了,在第十一章 Odoo 12开发之看板视图和用户端 QWeb中将详细介绍看板视图,它会将记录可视化为卡片形式,甚至会按列组织为看板。下面我们将学习一些其它视图类型:
- activity将计划活动显示为有组织的汇总
- calendar基于所选日期字段以日历格式展示数据
- diagram展示记录间的关系,当前不在 Odoo 中使用
以下两种视图类型用于显示累加数据:
- graph用于图表展示
- pivot用于交互的数据透视表
还有更多的视图类型,但仅在 Odoo 企业版中可用。因为我们整个系列的文章是基于社区版的,所以无法为这些视图提供示例:
- dashboard使用透视表和图表这类子视图展示累加数据
- cohort用于显示在不同时期数据如何变化
- gantt以甘特图显示日期计划信息,常用于项目管理
- grid通过行和列网格组织数据进行展示
官方文档中提供了对所有视图和可用属性很好的参考,这里就不再重复。我们集中于提供一些基础使用示例,这样可以对这些视图入门。这样应该可以提供一个很好的基础,然后可进一步探索每个视图的所有功能。
小贴士:可通过社区插件模块查看其它视图类型。OCA 管理的网页客户端插件请见 GitHub 仓库。例如,web_timeline模块提供了一个时间线视图类型,也可像甘特图那样展示计划信息,它是社区版的 gantt 视图类型。
活动视图
活动视图类型是内置的计划活动汇总板,帮助用于可视化活动任务。由 mail 模块提供,因此需要先安装该模块才能使用这一视图类型。要使用这一类型,只需在窗口操作的 view_code 属性的视图列表中添加活动视图类型即可。实际的视图定义会自动生成,我们也可以手动进行添加,唯一的选项是修改 string 属性,但在UI 中并不使用。
作为参考,活动视图的定义类似这样:
1 |
<activity string="Activities"/> |
日历视图
从名称可以看出,该视图类型在日历中展示记录,可通过不同时间区间浏览:按月、按周或按日。以下是我们图书借阅的日历视图,根据请求日期在日历上显示各项:
1 2 3 4 5 6 7 8 9 10 |
<record id="view_calendar_checkout" model="ir.ui.view"> <field name="model">library.checkout</field> <field name="arch" type="xml"> <calendar date_start="request_date" color="user_id"> <field name="member_id" /> <field name="stage_id" /> </calendar> </field> </record> |
补充:请记得在菜单xml文件中加回前文删除的 calendar 类型
基础的日历属性有:
- date_start是开始日期字段(必填)
- date_end是结束日期字段(可选)
- date_delay是天数字段,用于代替date_end
- all_day传入一个布尔字段名,用于标识全天活动。这类活动会忽略时长。
- color用于为一组日历项添加颜色。每个不同值都会被分配一种颜色,它的所有项都会显示为相同颜色。
- mode是日历视图的默认显示模块,可以是天、周或月。
ℹ️Odoo 11中的修改
dipsplay 日历属性在 Odoo 11中删除。此前的版本中,它用于自定义日历项标题文本的格式,例如display=”[name], Stage [stage_id]”。
透视表视图
还可通过透视表查看数据,它是一个动态分析矩阵。为此我们可使用透视表视图。
ℹ️Odoo 9中的修改
透视表在 Odoo 8中就已存在,作为一个图表视图功能。在 Odoo 9中,它成为一个独立的视图类型。同时也增强了透视表功能、优化了透视表数据的获取。
数据累加仅对数据库中存储的字段可用。我们将使用num_books字段来展示一些借书数量的统计。它是一个计算字段,还没有存储在数据库中。要在这些视图中使用,需要通过添加store=True属性先将其存储在数据库中:
1 2 3 |
num_books = fields.Integer( compute='_compute_num_books', store=True) |
使用如下代码来为图书借阅添加数据透视表:
1 2 3 4 5 6 7 8 9 10 11 |
<record id="view_pivot_checkout" model="ir.ui.view"> <field name="model">library.checkout</field> <field name="arch" type="xml"> <pivot> <field name="stage_id" type="col" /> <field name="member_id" /> <field name="request_date" interval="week" /> <field name="num_books" type="measure" /> </pivot> </field> </record> |
图表和透视表视图应包含描述轴和度量的字段元素,两者的属性大多数都通用:
- name像其它视图一样标识图表中使用的字段
- type是指如何使用字段,行分组(默认)、度量(measure)或列(仅针对透视表,用于列分组)
- interval用于日期字段,是对时间数据的分组间隔:按天、按周、按月、按季度或按年
图表视图
图表视图将数据累加展示为图表,可以使用柱状图、线状图和饼图。下面来为图书借阅添加图表视图:
1 2 3 4 5 6 7 8 9 |
<record id="view_graph_checkout" model="ir.ui.view"> <field name="model">library.checkout</field> <field name="arch" type="xml"> <graph type="bar"> <field name="stage_id" /> <field name="num_books" type="measure" /> </graph> </field> </record> |
图表视图元素可带有一个type属性,值可为 bar(默认), pie或line。对于 bar,可使用额外的stacked=”True”属性来让柱状图叠放起来。图表使用两种类型字段:
- type=”row”是默认值,设置累加值的条件
- type=”measure”用于作为实际累加值的度量字段
图表和透视表视图应包含描述需使用的轴和度量的字段元素。大多数图表视图中的属性同样可在透视表视图中使用。
总结
本文中我们学习了更多创建用户界面的 Odoo 视图。我们深入讲解了表单视图,然后一起概览了其它视图类型,包括列表视图和搜索视图。我们还学习了如何向视图元素添加动态行为。
下一篇文章中,我们将学习本文中未涉及到的视图:看板视图以及它使用的模板语言 QWeb。
☞☞☞第十一章 Odoo 12开发之看板视图和用户端 QWeb
扩展阅读
以下本文中所讨论的话题的附加参考和补充材料:
def button_done(self): Stage = self.env['library.checkout.stage'] done_stage = State.search( [('state', '=', 'done')], limit=1) for checkout in self: checkout.stage_id = done_stage return True
State.search( 应为Stage.search