第 1 章:Odoo 视图概览
1.1. Odoo 视图是什么?它们在 Odoo 应用中的作用是什么?
在 Odoo 框架中,视图(Views)是用户界面的核心组成部分,它们定义了数据模型中的信息如何呈现给最终用户。本质上,视图充当了存储在数据库中的原始数据与用户友好、可交互的界面之间的桥梁 。每一个 Odoo 应用,无论是销售、库存、会计还是自定义模块,都依赖视图来展示列表、表单、图表、看板等多种数据展现形式。
Odoo 的一个显著特点是,同一个数据模型可以拥有多种不同的视图。这意味着相同的数据集可以根据用户的需求或任务场景,以多种方式进行组织和显示 。例如,一个产品模型可能同时拥有一个列表视图(用于快速浏览和筛选产品)、一个表单视图(用于查看和编辑单个产品的详细信息)以及一个看板视图(用于按类别或阶段可视化产品)。这种灵活性是 Odoo 强大可定制性的体现。
视图的定义完全基于 XML。Odoo 系统会动态地根据这些 XML 定义来创建和渲染用户界面 。这种声明式的方法使得开发者能够通过编写结构化的 XML 代码来精确控制界面的布局和行为,而无需深入到底层渲染逻辑。
理解视图的这一核心角色至关重要,因为它们是用户与 Odoo 系统中数据进行交互的主要途径。一个设计良好、功能恰当的视图能够极大地提升应用的用户体验和操作效率。反之,如果视图设计不当,即便底层数据模型和业务逻辑再完善,用户也可能难以高效地使用系统。因此,掌握视图的定义、类型和定制方法是每一位 Odoo 开发者和学习者的必备技能。
1.2. Odoo 的 MVC(模型-视图-控制器)架构概述
Odoo 的整体架构遵循经典的 MVC(Model-View-Controller)设计模式,这是一种广泛应用于现代软件开发的架构模式,旨在分离应用程序的不同关注点,从而提高模块化、可维护性和可扩展性。
- 模型(Model): 模型层是 Odoo 应用的核心,负责处理数据和业务逻辑。它直接与数据库交互,定义了数据的结构(如字段、关系)、数据的约束(如必填项、唯一性)以及与数据相关的操作(如创建、读取、更新、删除记录的方法,以及更复杂的业务规则)。在 Odoo 中,模型通常是用 Python 类定义的 。
- 视图(View): 视图层,即本指南的重点,负责数据的表示和用户界面的呈现。如前所述,视图是用 XML 定义的,它们决定了用户在屏幕上看到什么以及如何看到这些信息 。视图从模型获取数据,并按照预定义的布局和样式将其展示出来。
- 控制器(Controller): 控制器层充当模型和视图之间的协调者。它接收用户的输入请求(例如,通过网页浏览器或 API),解析这些请求,然后调用模型层相应的方法来处理数据或执行业务逻辑。处理完毕后,控制器会选择一个或多个合适的视图,并将模型层返回的数据传递给视图进行渲染,最终将生成的界面呈现给用户。Odoo 中的控制器可以分为两类:服务器端控制器(Python编写,处理 HTTP 请求和路由)和客户端控制器(JavaScript编写,例如在 OWL 框架下管理复杂视图的交互逻辑)。窗口动作(Window Actions)在某种程度上也扮演了控制器的角色,它们定义了当用户点击菜单项或按钮时,系统应如何响应,例如打开哪个模型的哪个视图 。
将视图置于 MVC 架构中进行理解,有助于开发者认识到视图并非孤立存在。它们与模型紧密耦合以获取和展示数据,同时受控于控制器(或动作)的调度和逻辑处理。例如,模型中新增一个字段,通常需要在视图中进行相应的修改才能将其展示给用户。同样,视图中复杂的交互行为(如看板卡片的拖拽、动态过滤等)往往需要客户端 JavaScript 控制器的支持。这种各司其职又相互协作的关系是 Odoo 框架高效运作的基础。
1.3. 视图在数据呈现和用户交互中的重要性
视图在 Odoo 应用中扮演着至关重要的角色,它们不仅是数据的展示窗口,更是用户与系统进行交互的核心媒介。其重要性体现在以下几个方面:
- 数据可视化与解读:原始数据通常以结构化的形式存储在数据库中,对于非技术用户而言可能难以直接理解。视图将这些原始数据转化为易于理解和消化的信息格式,如列表、表单、图表、日历等 。通过合理的布局、字段选择和样式应用,视图能够突出关键信息,帮助用户快速把握业务状况。
- 用户工作流的引导:视图的设计直接影响用户在系统中的操作流程。例如,表单视图通过字段的分组、排列以及按钮的设置,引导用户按照特定的顺序输入和处理数据 。看板视图则通过列的划分和卡片的拖拽,直观地反映了任务或流程的状态变迁,引导用户推进工作。
- 交互能力的提供:视图不仅仅是静态的展示,它们还提供了丰富的交互功能。用户可以通过视图进行记录的创建、编辑、删除 ,执行特定的业务操作(通过按钮触发模型方法或窗口动作),进行数据的筛选、排序、分组 ,以及导航到关联记录等。这些交互能力是用户完成日常工作的基本保障。
- 用户体验(UX)的关键:视图的质量直接决定了应用的用户体验。一个清晰、直观、响应迅速且符合用户操作习惯的视图能够显著提高用户满意度和工作效率。反之,混乱、复杂或功能缺失的视图则会导致用户困惑、操作效率低下,甚至对系统产生抵触情绪。Odoo Studio 等工具允许对视图进行一定程度的自定义,以适应特定用户或企业的需求,这本身也说明了视图对用户体验的重要性 。
- 上下文感知与权限控制:视图的呈现和行为可以根据当前的上下文(如用户、公司、激活的记录等)以及用户的访问权限动态调整 。例如,某些字段或按钮可能只对特定用户组可见或可编辑。这种动态性确保了数据安全性和操作的合规性,同时也使得界面更加贴合用户的实际工作场景。
综上所述,视图是 Odoo 应用中连接数据与用户的关键枢纽。开发者在设计和实现视图时,不仅要考虑技术层面的 XML 结构,更要从用户需求和业务流程出发,力求打造出既美观又实用的用户界面。视图的设计并非仅仅是技术实现,它更是一门关乎功能性、易用性和效率的艺术。正如 Odoo 18 中对联系人表单视图进行的改造 ,这表明 Odoo 自身也在不断优化核心视图,以提升整体用户体验。
第 2 章:Odoo 18 核心视图类型详解
Odoo 18 提供了多种核心视图类型,每种视图都有其特定的用途和展现数据的最佳方式。本章将详细探讨这些视图类型,包括它们的用途、XML 结构、关键元素和常用属性,并结合代码示例进行说明。
2.1. 列表视图(List/Tree View)
- 用途与使用场景:
列表视图(在旧版本中常被称为树状视图 Tree View)用于以表格形式展示多条记录 。每一行代表一条数据库记录,每一列显示记录的一个字段值。这种视图非常适合数据概览、快速查找记录、对简单记录进行批量编辑以及进行排序、筛选和分组操作 。
- XML 结构:
列表视图的根标签是 <list>
(在一些文档或旧代码中可能仍见到 <tree>
,但 Odoo 18 的文档和新示例倾向于使用 <list>
)。在 <list>
标签内部,通过 <field>
元素定义表格的列。
XML 代码示例:
<record id="view_student_info_list" model="ir.ui.view">
<field name="name">student.info.view.list</field>
<field name="model">student.info</field>
<field name="arch" type="xml">
<list string="学生列表" editable="bottom" default_order="name asc" decoration-danger="age > 25">
<field name="name"/>
<field name="age" sum="平均年龄"/>
<field name="date_of_birth" optional="hide"/>
<field name="department"/>
<field name="active" invisible="1"/>
<button name="action_toggle_active" type="object" icon="fa-check"/>
</list>
</field>
</record>
- 关键元素与属性:
<list>
(根元素):string
:视图的标题,可选 。editable
:允许行内编辑,可选值为top
(新记录在顶部创建) 或bottom
(新记录在底部创建) 。default_order
:默认排序字段,例如name asc
或date desc, name asc
。decoration-{style}
:根据条件动态改变行的样式。可用的样式有bf
(粗体),it
(斜体),danger
(淡红色),info
(淡蓝色),muted
(淡灰色),primary
(淡紫色),success
(淡绿色),warning
(淡褐色) 。例如:decoration-success="state == 'done'"
。limit
:每页显示的记录数,默认为 80 。create
/edit
/delete
:布尔值,控制是否允许通过此视图创建、编辑、删除记录,默认为True
。import
:布尔值,控制是否允许导入记录,默认为True
。export_xlsx
:布尔值,控制是否允许导出为 XLSX 文件,默认为True
。multi_edit
:设为'1'
以激活多记录编辑功能 。sample
:布尔值,当没有记录时,是否显示示例数据,默认为False
。
<field>
(列定义):name
:对应模型中的字段名,必需 。string
:列的显示标题,如果未提供,则使用模型中字段的标签 。widget
:指定用于显示或编辑该字段的特定部件(widget) 。例如progressbar
,handle
,monetary
。sum
/avg
:在列脚显示该列的总计或平均值,并可指定标签,例如sum="总金额"
。invisible
:设为'1'
或动态表达式,控制字段(单元格内容)是否可见 。column_invisible
:设为'1'
或动态表达式,控制整个列是否可见。与invisible
不同,它作用于整个列,并且其表达式的求值不依赖于当前行的具体值 。optional
:使列的可见性可由用户配置。可选值为show
(默认显示) 或hide
(默认隐藏) 。用户可以在“可选列”菜单中切换其可见性。readonly
:设为'1'
或动态表达式,使字段在行内编辑时为只读 。groups
:限制此列对特定用户组可见 。decoration-{style}
:类似<list>
标签的同名属性,但作用于单个单元格 。
<button>
(行内按钮):name
:按钮调用的方法名 (如果type="object"
) 或动作的 XML ID (如果type="action"
) 。type
:按钮类型,object
或action
。string
:按钮上显示的文本。icon
:按钮上显示的 Font Awesome 图标,例如fa-pencil
。class
:CSS 类。attrs
:动态控制按钮属性 (如invisible
)。
<header>
(视图控制面板按钮):- 用于在列表视图的控制面板(通常在搜索栏下方)添加自定义按钮。
- 内部可以包含
<button>
元素 。 <button>
的display
属性 (display
或always
) 控制按钮何时可见 。
列表视图的高度可配置性体现在其对列显示(通过 invisible
, column_invisible
, optional
属性)和行内交互(通过 editable
属性和行内按钮)的精细控制。特别是 column_invisible
属性,它允许基于非记录特定条件(例如用户权限或全局配置)隐藏整个数据列,这对于构建响应式布局或根据上下文裁剪信息显示非常有用,而无需从视图的数据集中完全移除该字段。同时,optional
属性赋予了最终用户一定的界面定制能力,使其可以根据个人偏好显示或隐藏次要信息列,从而优化信息密度和可读性。
2.2. 表单视图(Form View)
- 用途与使用场景:
表单视图用于显示和编辑单个记录的完整详细信息 。它是数据录入、修改和查阅特定记录内容的主要界面。
- XML 结构:
表单视图的根标签是 <form>
。内部通常包含结构化元素如 <header>
(用于放置状态栏和工作流按钮), <sheet>
(用于主要内容区域,提供类似纸张的响应式布局), <footer>
(用于对话框按钮), 以及用于组织字段的 <group>
, <notebook>
, <page>
等。字段通过 <field>
标签显示,按钮通过 <button>
标签定义。
XML 代码示例:
<record id="view_student_info_form" model="ir.ui.view">
<field name="name">student.info.view.form</field>
<field name="model">student.info</field>
<field name="arch" type="xml">
<form string="学生档案" create="true" edit="true" delete="true" duplicate="true" disable_autofocus="0">
<header>
<button name="action_confirm_enrollment" type="object" string="确认入学" class="oe_highlight" attrs="{'invisible': [('state', '!=', 'draft')]}"/>
<button name="action_graduate" type="object" string="标记毕业" states="enrolled"/>
<field name="state" widget="statusbar" statusbar_visible="draft,enrolled,graduated,cancelled"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="action_view_courses" type="object" class="oe_stat_button" icon="fa-graduation-cap">
<field name="course_count" widget="statinfo" string="所修课程"/>
</button>
</div>
<widget name="web_ribbon" title="优秀学生" bg_color="bg-success" invisible="is_excellent == False"/>
<div class="oe_avatar">
<field name="image_1920" widget="image"/>
</div>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1>
<field name="name" placeholder="例如:张三" default_focus="1"/>
</h1>
<field name="category_ids" widget="many2many_tags" options="{'color_field': 'color'}" placeholder="标签..."/>
</div>
<group>
<group string="基本信息">
<field name="student_id_number"/>
<field name="gender"/>
<field name="age" readonly="1"/>
</group>
<group string="联系方式">
<field name="email" widget="email"/>
<field name="phone" widget="phone"/>
</group>
</group>
<notebook>
<page string="详细信息" name="detailed_info">
<group>
<field name="address" placeholder="详细街道地址..."/>
<field name="date_of_birth"/>
<field name="nationality_id" options="{'no_open': True, 'no_create': True}"/>
</group>
<separator string="备注"/>
<field name="notes" nolabel="1" placeholder="其他备注信息..."/>
</page>
<page string="成绩记录" name="grades">
<field name="grade_ids">
<tree editable="bottom">
<field name="course_id"/>
<field name="score"/>
<field name="grade_date"/>
</tree>
</field>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
- 关键元素与属性:
<form>
(根元素):string
:视图标题 。create
/edit
/delete
/duplicate
:布尔值,控制是否允许创建、编辑、删除、复制操作,默认为True
。groups
:逗号分隔的用户组 XML ID 列表,限制对此表单视图的访问 。js_class
:指定一个 JavaScript 类名,用于自定义表单视图的客户端行为 。disable_autofocus
:布尔值,若为True
,则禁用视图加载时自动聚焦到第一个可编辑字段的行为,默认为False
。
<header>
:通常位于<sheet>
之前,用于放置工作流按钮和状态栏 (statusbar
widget) 。<sheet>
:表单的主要内容区域,提供响应式布局 。<footer>
:用于在对话框模式下显示按钮,如“保存”、“取消” 。<group>
:用于将字段组织成列(默认为2列)。可以嵌套,并可用col
和colspan
属性控制布局 。string
:可选,组的标题。
<notebook>
:创建一个选项卡式界面 。<page>
:定义<notebook>
中的一个选项卡。string
:选项卡的标题 。name
:选项卡的技术名称,用于继承或代码引用。invisible
:动态隐藏选项卡。
<field>
:显示模型的一个字段。name
:模型字段名,必需 。widget
:指定特殊的小部件(widget)来渲染字段,如image
,many2many_tags
,statusbar
,monetary
,email
,phone
,priority
等 。string
:字段标签。placeholder
:字段为空时显示的提示文字 。nolabel
:设为'1'
时不显示字段标签(通常在<group>
内使用)。readonly
/required
/invisible
:布尔值或动态表达式,控制字段的只读、必填、可见状态 。attrs
:一个字典,用于根据记录值动态改变字段的readonly
,required
,invisible
属性。options
:传递给小部件的 JSON 格式选项,例如{'no_open': True, 'no_create_edit': True}
用于关系字段 。domain
:用于关系字段,在选择或搜索关联记录时施加过滤条件 。context
:用于关系字段,在打开关联记录或创建新关联记录时传递上下文信息 。default_focus
:设为'1'
使该字段在视图加载时获得焦点 。class
:CSS 类,如oe_inline
(内联显示),oe_title
(用于标题字段),oe_avatar
(头像图片) 。
<button>
:定义一个按钮。name
:方法名 (type="object"
) 或动作 XML ID (type="action"
) 。string
:按钮文本。type
:object
(调用 Python 方法) 或action
(执行一个窗口动作) 。icon
:Font Awesome 图标。class
:CSS 类,如oe_highlight
(高亮按钮),oe_link
(链接样式按钮)。special
:用于对话框,值为save
或cancel
。confirm
:在执行动作前显示的确认信息 。attrs
:动态控制按钮属性。states
(已弃用,但旧代码中可能存在):根据状态字段的值控制按钮的可见性,推荐使用attrs
。
<label for="field_name"/>
:显式地为某个字段创建标签,当字段未使用<group>
自动布局或nolabel="1"
时使用 。<div class="oe_chatter">
:用于集成 Odoo 的消息、活动和关注者系统 (需要模型继承mail.thread
) 。- Odoo 18 特定变更:根据 Odoo 18.1 的发布说明,联系人(
res.partner
)的表单视图和模型进行了重做 。这意味着开发者在处理联系人相关的自定义或继承时,需要特别关注这些变化,可能涉及字段的增删、布局的调整或底层逻辑的变更。
表单视图通过这些结构化元素,使得复杂信息的组织和呈现更为有序和用户友好。例如,<sheet>
提供了类似纸张的文档感,<group>
实现了字段的对齐和分栏,而 <notebook>
则有效地将大量信息分解到不同的选项卡中,避免了界面过于冗长。oe_title
样式类和 <h1>
标签通常用于表单的主要名称字段,这已成为 Odoo 表单设计的惯例,有助于建立清晰的视觉层级 。对核心视图(如联系人表单)的持续改进也反映了 Odoo 对提升用户体验的重视。
2.3. 看板视图(Kanban View)
- 用途与使用场景:
看板视图以“卡片”(cards)的形式展示记录,这些卡片通常被组织在不同的列(columns)中,代表流程的不同阶段或类别 。这种视图对于可视化工作流(如销售管道、项目任务管理、招聘流程)非常有效,用户可以通过拖拽卡片来更新记录的状态。如果存在看板视图,它通常会作为移动设备上列表视图的默认替代方案 。
- XML 结构:
看板视图的根标签是 <kanban>
。其核心内容由 <templates>
定义,内部使用 QWeb 模板语言来设计卡片的布局和内容。通常会有一个名为 kanban-box
或 card
的主模板 。看板视图还可以包含 <field>
声明(用于那些在模板中不直接显示但逻辑上需要的字段)、<progressbar>
(用于在列顶部显示进度条)和 <header>
(用于控制面板按钮)。
XML 代码示例:
<record id="project_task_kanban_view" model="ir.ui.view">
<field name="name">project.task.kanban</field>
<field name="model">project.task</field>
<field name="arch" type="xml">
<kanban class="o_kanban_mobile" default_group_by="stage_id" quick_create="true" records_draggable="true" group_create="false">
<field name="name"/>
<field name="user_ids"/>
<field name="project_id"/>
<field name="priority"/>
<field name="kanban_state"/>
<field name="color"/>
<field name="deadline"/>
<progressbar field="kanban_state" colors='{"done": "success", "blocked": "danger", "normal": "muted"}' sum_field="total_hours_spent"/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_card oe_kanban_global_click {{ record.kanban_state.raw_value == 'blocked'? 'oe_kanban_card_danger' : '' }}">
<div class="oe_kanban_content">
<div class="o_kanban_record_top">
<div class="o_kanban_record_headings">
<strong class="o_kanban_record_title">
<field name="name"/>
</strong>
<small class="o_kanban_record_subtitle" t-if="record.project_id.value">
<field name="project_id"/>
</small>
</div>
<field name="priority" widget="priority"/>
</div>
<div class="o_kanban_tags">
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
</div>
<div t-if="record.deadline.value" class="text-muted">
Deadline: <field name="deadline" widget="date"/>
</div>
<div class="o_kanban_record_bottom">
<div class="oe_kanban_bottom_left">
<field name="user_ids" widget="many2many_avatar_user"/>
</div>
<div class="oe_kanban_bottom_right">
<widget name="kanban_color_picker" field="color"/>
<img t-att-src="kanban_image('res.users', 'avatar_128', record.user_ids.raw_value)" t-if="record.user_ids.raw_value.length > 0" class="oe_avatar oe_kanban_avatar" alt="Avatar"/>
</div>
</div>
</div>
<div class="oe_clear"></div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
- 关键元素与属性:
<kanban>
(根元素):default_group_by
:指定默认用于分组的字段名,这些组会形成看板的列 。default_order
:卡片在列内的默认排序 。class
:CSS 类,例如o_kanban_mobile
用于优化移动端显示 。quick_create
:布尔值,是否允许在列中快速创建记录(通常是一个简化的表单),默认为True
(当按 many2one, selection, char, boolean 字段分组时)。quick_create_view
:用于快速创建的表单视图的 XML ID 。on_create
:点击“创建”按钮时调用的自定义动作的名称 。group_create
/group_delete
/group_edit
:布尔值,控制是否允许用户创建、删除、编辑列(分组),默认为True
。records_draggable
:布尔值,当记录分组时,是否允许拖拽卡片到其他列,默认为True
。archivable
:布尔值,如果模型有active
字段,是否允许归档/取消归档记录,默认为True
。js_class
:指定一个 JavaScript (OWL) 控制器类,用于扩展或完全替换看板视图的默认客户端行为 。sample
:布尔值,当没有记录时,是否显示示例数据 。
<field>
(在<kanban>
标签下,<templates>
之外):- 声明那些在 QWeb 模板中需要通过
record.field_name.value
或record.field_name.raw_value
访问,但可能不直接显示或以特殊方式显示的字段 。
- 声明那些在 QWeb 模板中需要通过
<progressbar>
:- 在看板列的顶部显示一个进度条,通常基于列内记录的某个字段进行聚合。
field
:用于对列内记录进行子分组并计算进度的字段名(通常是 selection 或 many2one 类型)。colors
:一个 JSON 字符串,映射field
的值到特定的颜色 (如success
,danger
,warning
,muted
) 。sum_field
:一个数字字段的名称,其总和将显示在进度条旁边 。
<templates>
:- 包含一个或多个 QWeb 模板 (
<t t-name="...">
) 。 - 必须定义一个根卡片模板,通常命名为
kanban-box
或card
。 - 内部可以使用标准的 QWeb 指令 (
t-foreach
,t-if
,t-attf
,t-esc
,t-raw
,t-set
) 和 HTML 标签来构建卡片布局。 - 卡片内的
<field>
:用于显示字段值。name
:字段名。widget
:指定小部件,如many2many_tags
,priority
,kanban_color_picker
,image
,monetary
等。handle
小部件用于在未分组的看板中手动排序卡片 。
- 按钮和链接:
<button>
或<a>
标签若带有type
属性,可以执行特殊操作 :type="object"
/type="action"
:类似表单视图中的按钮。type="edit"
:以编辑模式打开记录的表单视图。type="open"
:以只读模式打开记录的表单视图。type="delete"
:删除当前记录(通常会有确认提示)。type="archive"
/type="unarchive"
:归档或取消归档记录。
- QWeb 上下文变量:模板中可访问
record
(包含字段值和原始值),widget
(包含editable
和deletable
等状态),read_only_mode
,luxon
(日期时间库),JSON
等 。
- 包含一个或多个 QWeb 模板 (
看板视图通过 XML 结构定义整体行为和数据获取,而通过 QWeb 模板赋予了卡片内容和布局极大的灵活性。js_class
属性的引入,使得开发者可以利用 Odoo Web Library (OWL) 创建自定义的 JavaScript 控制器和渲染器,从而实现标准 XML 和 QWeb 难以达成的复杂客户端交互和动态行为 。这对于构建高度定制化和交互丰富的看板应用至关重要,意味着高级看板定制可能需要开发者具备 JavaScript/OWL 的知识。
2.4. 搜索视图(Search View)
- 用途与使用场景:
搜索视图为其他视图(主要是列表、看板、图表、透视表等集合视图)提供筛选、分组和搜索记录的功能 。它本身不直接展示记录,而是作为一种辅助工具,增强用户在大量数据中定位和分析特定信息的能力。
- XML 结构:
搜索视图的根标签是 <search>
。内部可以包含 <field>
(用于定义用户可输入值的搜索字段)、<filter>
(用于定义预设的筛选条件或分组选项)、<separator>
(视觉分隔符) 和 <group>
(逻辑分组筛选器,已不常用,通常用 <separator>
或直接排列),以及 <searchpanel>
(用于创建左侧的分类筛选面板)。
XML 代码示例:
<record id="view_student_info_search" model="ir.ui.view">
<field name="name">student.info.view.search</field>
<field name="model">student.info</field>
<field name="arch" type="xml">
<search string="搜索学生">
<field name="name" string="姓名/学号" filter_domain="['|', ('name', 'ilike', self), ('student_id_number', 'ilike', self)]"/>
<field name="course_id" string="课程" domain="" context="{'search_course_by_code': True}"/>
<field name="department_id" operator="child_of"/>
<field name="enrollment_date"/>
<separator/>
<filter string="已激活" name="active_students" domain="" help="显示所有已激活的学生"/>
<filter string="未激活" name="inactive_students" domain="[('active', '=', False)]"/>
<separator/>
<filter string="入学日期" name="filter_enrollment_date" date="enrollment_date"/>
<group expand="0" string="分组依据">
<filter string="按系别" name="groupby_department" context="{'group_by': 'department_id'}"/>
<filter string="按课程" name="groupby_course" context="{'group_by': 'course_id'}"/>
<filter string="按入学月份" name="groupby_enrollment_month" context="{'group_by': 'enrollment_date:month'}"/>
</group>
<searchpanel>
<field name="department_id" string="系别" icon="fa-university" enable_counters="1" select="multi"/>
<field name="course_id" string="课程" icon="fa-book" select="one" hierarchize="0" limit="100"/>
<field name="gender" string="性别" icon="fa-venus-mars" expand="1"/>
</searchpanel>
</search>
</field>
</record>
- 关键元素与属性:
<search>
(根元素):string
:搜索视图的描述性标题,通常显示在搜索框的占位符或高级搜索菜单中 。
<field>
(可搜索字段):name
:模型中字段的名称,用户将在此字段上进行搜索 。string
:在搜索建议中显示的标签 。operator
:当用户输入值后,用于构建域的默认操作符,如=
,ilike
,>
,<
等。默认为=
。filter_domain
:一个 Python 表达式,用于构建自定义的搜索域。它比operator
更强大,允许基于用户输入 (self
) 构建复杂的域,例如跨多个字段搜索。如果提供,则优先于operator
。例如:filter_domain="[('name', 'ilike', self)]"
。context
:一个 Python 字典,当用户与此搜索字段交互时(例如,在 many2one 字段中选择值时),此上下文会合并到目标视图的上下文中 。domain
:用于关系型字段(如 many2one)的自动完成建议列表的过滤条件 。groups
:限制此搜索字段对特定用户组可见 。invisible
:设为'1'
或动态表达式,隐藏此搜索字段 。
<filter>
(预定义筛选器/分组器):name
:筛选器的技术名称,用于代码引用或search_default_
上下文键 。string
:显示给用户的筛选器标签 。domain
:当此筛选器被激活时,附加到当前搜索域的域表达式 。context
:当此筛选器被激活时,合并到当前上下文的 Python 字典。常用于定义分组依据,例如context="{'group_by': 'field_name'}"
或context="{'group_by': ['field1', 'field2']}"
。date
:指定一个日期或日期时间字段的名称。Odoo 会自动为此字段生成一组预定义的日期范围筛选器(如本月、上季度、去年等)。default_period
:与date
属性一起使用,指定默认选中的日期范围(如month
,year
)。groups
:限制此筛选器对特定用户组可见 。help
:鼠标悬停时显示的帮助文本 。
<separator/>
:在“筛选”下拉菜单中添加一条视觉分隔线 。<group string="Group Title"/>
:在“分组依据”下拉菜单中创建一个子菜单,用于组织相关的分组选项。内部可以包含<filter>
元素(通常只带context="{'group_by': '...'}"
)。expand="0"
(或"1"
):控制该分组默认是否展开。
<searchpanel>
:- 在列表视图或看板视图的左侧(或右侧,取决于语言方向)显示一个或多个筛选面板,提供类似分面搜索的体验 。
- 内部只能包含
<field>
元素。 <field>
(在<searchpanel>
内):name
:要在面板中作为筛选依据的字段名(通常是 many2one, many2many, selection 类型)。string
:面板中此字段筛选器的标题 。select
:定义选择行为和显示。'one'
表示单选(通常用于 many2one, selection),'multi'
表示多选(通常用于 many2many, selection, 某些情况下的 many2one)。icon
:为筛选器类别显示的 Font Awesome 图标 。color
:字段名,其值用于为筛选器类别着色 。enable_counters
:布尔值 ('1'
或'0'
),是否在每个筛选值旁边显示匹配的记录数,默认为False
。expand
:布尔值 ('1'
或'0'
),筛选面板中的类别是否默认展开,默认为False
。hierarchize
:布尔值 ('1'
或'0'
),对于具有层级关系的 many2one 字段(如产品类别),是否以层级结构显示筛选值,默认为True
。limit
:获取并显示的最大筛选值的数量,默认为 200 。domain
:一个域表达式,用于限制此搜索面板字段中显示的筛选值 。groupby
:一个字段名,用于对搜索面板中的值进行分组(例如,按国家对城市进行分组)。
搜索视图通过 filter_domain
属性极大地增强了用户输入搜索的灵活性。它允许开发者预先定义复杂的查询逻辑,将用户的单一输入(self
)映射到对多个字段的搜索,或者应用特定的比较操作符,从而使用户无需了解底层数据结构即可高效检索信息。例如,在一个产品搜索字段中,filter_domain="['|', ('name', 'ilike', self), ('default_code', 'ilike', self)]"
可以让用户通过输入产品名称或内部参考号来查找产品。此外,<searchpanel>
的引入为用户提供了一种非常直观和交互式的筛选方式,尤其适用于具有明确分类维度的数据集,显著提升了数据探索的效率。通过在动作的上下文中设置 search_default_
前缀的键,可以预先激活某些筛选器或搜索字段,从而定制视图的初始筛选状态 。
2.5. 图表视图(Graph View)
- 用途与使用场景:
图表视图用于将记录数据聚合后以图形方式(如条形图、饼图、折线图)进行可视化展示 。它有助于用户快速识别数据中的模式、趋势、比较和异常值,从而做出更明智的决策。
- XML 结构:
图表视图的根标签是 <graph>
。在 <graph>
标签内部,通过 <field>
元素来定义图表的维度(用于分组)和度量(用于聚合计算)。
XML 代码示例:
<record id="view_student_enrollment_graph" model="ir.ui.view">
<field name="name">student.enrollment.view.graph</field>
<field name="model">student.enrollment</field> <field name="arch" type="xml">
<graph string="招生分析" type="bar" stacked="True" sample="1" disable_linking="0" order="asc">
<field name="course_id" type="row"/> <field name="enrollment_month" type="col" interval="month"/> <field name="student_count" type="measure" operator="sum"/> </graph>
</field>
</record>
(注: enrollment_month
假设是一个日期字段,student_count
假设是一个数值字段或通过 _compute
得到的计数字段。实际使用时,type="row"
和 type="col"
的含义会根据图表类型 (bar
, pie
, line
) 和字段数量有所不同。通常第一个字段是主要分组依据,第二个字段(如果存在且图表类型支持)用于次级分组或系列。)
- 关键元素与属性:
<graph>
(根元素):string
:视图的标题 。type
:图表的类型,可选值为bar
(条形图,默认),pie
(饼图),line
(折线图) 。stacked
:布尔值 ('True'
或'False'
),仅用于条形图。如果为True
,则当有多个度量或第二个分组维度时,条形会堆叠显示,默认为True
。设为'0'
(等同于False
) 可阻止堆叠 。disable_linking
:设为'1'
(或True
) 时,图表元素(如条形、扇区)将不可点击以跳转到对应的列表视图,默认为False
。order
:X 轴值的排序方式,可选值为asc
(升序) 或desc
(降序) 。sample
:布尔值,当没有记录时,是否显示示例图表,默认为False
。
<field>
(维度或度量定义):name
:模型中字段的名称 。type
:定义字段在图表中的角色 。row
:通常作为第一个分组维度(例如,条形图的 X 轴类别,饼图的扇区)。col
:通常作为第二个分组维度(例如,在条形图中用于生成堆叠或并列的条形系列,或在折线图中生成不同的线条)。measure
:表示这是一个要进行聚合计算的数值字段(例如,Y 轴的值)。
interval
:仅用于日期 (date) 或日期时间 (datetime) 类型的分组字段。指定分组的时间间隔,可选值为day
,week
,month
,quarter
,year
。默认为month
。operator
:用于type="measure"
的字段,指定聚合函数,如sum
(默认),avg
,min
,max
,count
。。string
:在图表界面(如图例、工具提示或轴标签)中显示的字段名称,如果未提供,则使用模型字段的标签 。invisible
:设为'1'
(或True
) 时,该字段将不会出现在可供选择的度量或维度列表中,默认为False
。widget
:指定用于格式化度量值的小部件,例如monetary
,float_time
。
图表视图的一个关键特性是其对日期/时间类型字段的处理。通过 interval
属性,开发者可以直接在视图层面指定按日、周、月、季度或年对数据进行分组,而无需在模型层面预先创建这些聚合周期字段。这极大地简化了时间序列分析图表的创建过程,使得视图定义更加简洁和灵活 。例如,一个销售订单模型只需要一个订单日期字段,图表视图就可以通过 interval
属性轻松生成按月销售额的条形图或折线图。
2.6. 透视表视图(Pivot View)
- 用途与使用场景:
透视表视图以交叉表(cross-tabulation)的形式展示聚合数据,允许用户从多个维度对数据进行分析、切片和钻取 。它非常适合进行多维数据汇总和比较,并且用户通常可以直接将透视表数据导出为 XLSX 文件进行进一步分析 。
- XML 结构:
透视表视图的根标签是 <pivot>
。在 <pivot>
标签内部,通过 <field>
元素来定义行维度、列维度和度量值。
XML 代码示例:
<record id="view_sales_analysis_pivot" model="ir.ui.view">
<field name="name">sales.analysis.view.pivot</field>
<field name="model">sale.report</field> <field name="arch" type="xml">
<pivot string="销售分析" disable_linking="0" display_quantity="1" sample="1" default_order="price_total desc">
<field name="product_id" type="row"/>
<field name="partner_id" type="row"/>
<field name="date" type="col" interval="month"/>
<field name="price_subtotal" type="measure" operator="sum" widget="monetary"/>
<field name="product_uom_qty" type="measure" operator="sum" string="销售数量"/>
</pivot>
</field>
</record>
- 关键元素与属性:
<pivot>
(根元素):string
:视图的标题 。disable_linking
:设为'1'
(或True
) 时,透视表单元格中的值将不可点击以跳转到对应的列表视图,默认为False
。display_quantity
:设为'1'
(或True
) 时,如果模型中存在合适的数量字段,则默认在度量中显示“数量”列,默认为False
。default_order
:指定默认的度量字段和排序方向(asc
或desc
)用于对结果进行排序。例如amount_total desc
。sample
:布尔值,当没有记录时,是否显示示例透视表,默认为False
。
<field>
(维度或度量定义):name
:模型中字段的名称 。type
:定义字段在透视表中的角色 。row
:作为行维度进行分组(默认值)。可以有多个type="row"
字段,形成层级行。col
:作为列维度进行分组。可以有多个type="col"
字段,形成层级列。measure
:表示这是一个要进行聚合计算的数值字段。
interval
:仅用于日期 (date) 或日期时间 (datetime) 类型的维度字段(行或列)。指定分组的时间间隔,可选值为day
,week
,month
,quarter
,year
。operator
:用于type="measure"
的字段,指定聚合函数,如sum
(默认),avg
,min
,max
,count
。(与图表视图类似,聚合是度量的核心)。string
:在透视表界面(如维度选择器、度量选择器、表头)中显示的字段名称 。invisible
:设为'1'
(或True
) 时,该字段将不会出现在可供选择的度量或维度列表中,默认为False
。widget
:指定用于格式化度量值的小部件,例如monetary
,float_time
,percentage
。
透视表的核心在于其通过 type
属性(row
, col
, measure
)对字段进行分类,从而构建出交叉分析的结构 。用户可以在界面上动态地添加、移除或重新排列这些维度和度量,实现灵活的数据探索。
Odoo 18 的预期改进:
根据一些前瞻性信息,Odoo 18 预计将增强透视表的交互性,引入“动态透视表编辑”功能,允许用户直接在界面上添加或编辑维度、度量和域 。此外,在特定场景如库存批次转移中,也提及了将提供透视表视图进行分析 。这些改进如果实装,将大大提升用户进行即席数据分析的便捷性,减少对开发者预定义视图的依赖,更贴近现代商业智能(BI)工具的自助服务理念。
2.7. 日历视图(Calendar View)
- 用途与使用场景:
日历视图用于将带有日期或时间信息的记录(如会议、预约、截止日期、事件等)显示在一个标准的日历界面上,支持按日、周、月、年查看 。它对于管理时间敏感的任务和活动至关重要。
- XML 结构:
日历视图的根标签是 <calendar>
。
XML 代码示例:
<record id="view_meeting_calendar" model="ir.ui.view">
<field name="name">meeting.calendar</field>
<field name="model">calendar.event</field> <field name="arch" type="xml">
<calendar string="会议日历"
date_start="start"
date_stop="stop"
date_delay="duration" all_day="allday"
color="user_id"
mode="week" event_open_popup="true"
quick_add="true"
quick_create_view="calendar.view_calendar_event_quick_form"
form_view_id="calendar.view_calendar_event_form"
scales="day,week,month">
<field name="name"/> <field name="partner_ids" widget="many2many_tags_avatar" invisible="0" avatar_field="image_128"/>
<field name="location" invisible="1"/> </calendar>
</field>
</record>
- 关键元素与属性:
<calendar>
(根元素):string
:视图的标题 。date_start
:必需,指定模型中代表事件开始日期/时间的字段名 。date_stop
:可选,指定模型中代表事件结束日期/时间的字段名。如果提供,事件在日历上将有明确的持续时间,并且通常可以拖拽调整结束时间 。date_delay
:可选,替代date_stop
。指定一个表示事件持续时长的数字字段(单位通常是天)。all_day
:可选,指定模型中一个布尔字段的名称,该字段指示事件是否为全天事件。全天事件通常显示在日历的顶部区域 。color
:可选,指定一个字段名(通常是 many2one 或 selection 类型,如user_id
,event_type_id
),日历将根据此字段的值为不同的事件分配不同的颜色,便于区分 。mode
:指定日历的默认显示模式,可选值为day
,week
,month
,year
。默认为month
。scales
:逗号分隔的字符串,定义用户可以切换的显示模式列表,例如day,week,month
。event_open_popup
:布尔值,如果为True
,点击事件时将在一个模态对话框(通常是简化的表单视图)中打开,而不是导航到完整的表单视图,默认为False
。quick_create
:布尔值,如果为True
,用户可以直接在日历的空白区域点击或拖拽来快速创建新事件,通常会弹出一个极简的创建框,默认为True
。quick_create_view_id
:指定用于快速创建事件的特定表单视图的 XML ID。如果未提供,将使用默认的快速创建对话框 。form_view_id
:指定在日历中创建或编辑事件时打开的完整表单视图的 XML ID 。create
/edit
/delete
:布尔值,控制是否允许通过此视图创建、编辑、删除事件,默认为True
。
<field>
(在<calendar>
标签下):- 声明需要在日历事件卡片上显示或在日历逻辑中使用的字段。
name
:字段名。invisible
:设为'1'
时,该字段的值不会直接显示在事件卡片上(但仍可用于如color
属性或在弹窗中显示)。avatar_field
:用于 many2many 或 many2one 字段,指定一个关联模型中的图片字段名,用于在事件卡片上显示头像(例如,参与者的头像)。filters
:设为'1'
时,如果该字段是可筛选的类型(如 many2one, selection),则会在日历视图的侧边栏(如果存在)生成一个基于此字段的筛选器 。
日历视图的 color
属性对于提升信息的可辨识度非常关键。当与一个合适的字段(如负责人 user_id
或事件类型 category_id
)关联时,系统会自动为不同字段值的事件应用不同的背景色或边框色。这使得用户能够迅速从密集的日程中区分出不同类别或归属的事件,极大地改善了视觉组织和信息获取效率 。虽然颜色数量有限,可能会出现颜色复用的情况,但其带来的直观性提升仍然非常显著。
2.8. 甘特图视图(Gantt View)
- 用途与使用场景:
甘特图视图以水平条形图的形式展示任务或记录及其在时间轴上的计划开始和结束日期、持续时间以及依赖关系 。它主要用于项目管理、生产排程、资源规划等场景,帮助用户可视化项目进度、任务关联和资源分配。此视图是 Odoo 企业版的功能 。
- XML 结构:
甘特图视图的根标签是 <gantt>
。
XML 代码示例:
<record id="view_project_task_gantt" model="ir.ui.view">
<field name="name">project.task.view.gantt</field>
<field name="model">project.task</field>
<field name="arch" type="xml">
<gantt string="项目任务甘特图"
date_start="planned_date_begin"
date_stop="planned_date_end"
default_group_by="project_id,user_ids"
progress="progress"
color="project_id"
default_scale="week"
scales="day,week,month,year"
consolidation="effective_hours"
consolidation_max="{'user_ids': 'allocated_hours'}"
consolidation_exclude="is_milestone"
dependency_field="depend_on_ids"
dependency_inverted_field="dependent_ids"
thumbnails="{'project_id': 'image_128', 'user_ids': 'avatar_128'}"
display_unavailability="1"
total_row="1"
collapse_first_level="0"
precision="{'day': 'hour:half', 'week': 'day:half', 'month': 'day:full'}"
pill_label="1"
sample="1">
<field name="name"/> <field name="project_id" invisible="1"/> <field name="user_ids" invisible="1"/> <templates>
<t t-name="gantt-popover">
<div><strong><t t-esc="name"/></strong></div>
<div>Project: <t t-esc="project_id"/></div>
<div>Assignees: <t t-esc="user_ids.map(u => u).join(', ')"/></div>
<div>Progress: <t t-esc="progress"/>%</div>
</t>
</templates>
</gantt>
</field>
</record>
- 关键元素与属性:
<gantt>
(根元素):string
:视图标题 。date_start
:必需,模型中代表任务开始日期/时间的字段名 。date_stop
:必需,模型中代表任务结束日期/时间的字段名 。default_group_by
:逗号分隔的字段列表,用于默认对任务进行分组(例如,按项目、按负责人)。progress
:可选,表示任务完成百分比的数字字段名 (0-100) 。甘特条上会相应显示进度。color
:可选,一个字段名,用于对不同的甘特条进行颜色编码 。default_scale
:默认的时间刻度,可选day
,week
,month
,year
。默认为month
。scales
:逗号分隔的字符串,定义用户可以切换的时间刻度列表 。precision
:一个 JSON 字符串,定义在不同时间刻度下拖拽任务时的对齐精度。例如"{'day': 'hour:half', 'week': 'day:half', 'month': 'day'}"
表示按天查看时,任务可以对齐到半小时;按周查看时,对齐到半天 。consolidation
:可选,一个字段名,其值将用于在分组行上显示某种形式的“负荷”或“合并”值(例如,每日已分配工时)。consolidation_max
:可选,一个 JSON 字符串,映射分组字段到其最大允许合并值的字段名。当合并值超过最大值时,通常会以红色高亮显示。例如"{'user_id': 'max_daily_hours'}"
。consolidation_exclude
:可选,一个布尔字段名。如果为True
,则该任务不计入合并计算 。dependency_field
:可选,一个 many2many 字段名,表示当前任务依赖的前置任务 。dependency_inverted_field
:如果定义了dependency_field
,则此字段也必须定义,表示依赖于当前任务的后置任务(通常是dependency_field
的反向关系字段)。display_unavailability
:布尔值 ('1'
或'0'
)。如果为True
,并且模型支持(通过gantt_unavailability
方法),则会在甘特图上标记出不可用时间段(例如,员工的周末或假期)。thumbnails
:可选,一个 JSON 字符串,映射分组字段名到其头像/图片字段名。例如"{'user_id': 'avatar_128', 'project_id': 'image_small'}"
。会在分组行标题旁边显示缩略图 。total_row
:布尔值,是否在底部显示一个包含总记录数的行,默认为False
。collapse_first_level
:布尔值,如果按单个字段分组,是否允许折叠每个分组行,默认为False
。pill_label
:布尔值,是否在甘特条(pill)上显示时间标签(尤其在周/月刻度下),默认为False
。create
/edit
/delete
/plan
:布尔值,控制是否允许创建、编辑、删除、计划(拖拽)操作,默认为True
。form_view_id
:指定创建或编辑任务时打开的表单视图的 XML ID 。sample
:布尔值,当没有记录时,是否显示示例甘特图 。
<field>
(在<gantt>
标签下):- 声明需要在甘特条上显示或在甘特图逻辑(如分组、颜色、进度)中使用的字段。
name
:字段名。invisible
:设为'1'
时,字段值不直接显示在甘特条上,但仍可用于其他属性。
<templates>
:- 可选,可以定义一个名为
gantt-popover
的 QWeb 模板,用于自定义鼠标悬停在甘特条上时显示的浮窗内容 。
- 可选,可以定义一个名为
甘特图视图通过 dependency_field
和 dependency_inverted_field
属性,能够直观地展示任务之间的前后置依赖关系,这对于复杂的项目规划至关重要。连同 display_unavailability
功能(它依赖于模型端提供不可用时间段的逻辑,例如通过 _gantt_unavailability
方法),使得甘特图不仅仅是一个时间表,更是一个有效的资源调度和瓶颈识别工具 。Odoo 18.1 的发布说明中提到了“甘特图:关键路径”功能 ,这预示着甘特图视图在项目分析能力上的进一步增强。此外,在库存模块中,也预期会使用甘特图和透视表来可视化和分析批次转移 。
2.9. 仪表盘视图(Dashboard View)
- 用途与使用场景:
仪表盘视图旨在将来自不同来源的关键绩效指标(KPIs)、图表、数据聚合和其他重要信息整合到一个单一的、易于概览的界面上 。它帮助用户(尤其是管理者)快速了解业务的整体状况、监控关键指标并及时发现问题或机遇。
- XML 结构与实现方式:
在 Odoo 中,后端仪表盘的实现通常不依赖于一个像 <dashboard>
这样的特定根 XML 标签来定义其全部内容。相反,它们更多地是通过以下组合方式构建的:
客户端动作 (ir.actions.client
):定义一个客户端动作,其 tag
属性指向一个自定义的 JavaScript (通常是 OWL) 组件 。这个组件负责整个仪表盘的渲染和逻辑。
<record id="my_custom_app_dashboard_action" model="ir.actions.client">
<field name="name">我的应用仪表盘</field>
<field name="tag">my_custom_app.dashboard</field> </record>
-
- JavaScript (OWL) 组件:开发者创建一个 OWL 组件,该组件包含获取数据(例如通过 ORM 调用模型的 Python 方法)、处理数据以及渲染仪表盘布局的逻辑 。
QWeb 模板 (XML):OWL 组件会使用 QWeb 模板来定义仪表盘的 HTML 结构。这些模板位于模块的 static/src/xml/
目录下,并通过组件的 template
属性引用 。
<templates>
<t t-name="my_custom_app.DashboardMain" owl="1">
<div class="o_my_dashboard container-fluid">
<h1><t t-esc="title"/></h1>
<div class="row">
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">总销售额</h5>
<p class="card-text display-4"><span t-esc="salesData.total_sales_formatted"/></p>
</div>
</div>
</div>
<div class="col-md-8">
<div class="card">
<div class="card-body">
<h5 class="card-title">月度销售趋势</h5>
<div class="o_graph_container" t-ref="sales_chart_container"/>
</div>
</div>
</div>
</div>
</div>
</t>
</templates>
-
- 菜单项 (
ir.ui.menu
):创建一个菜单项,其action
属性指向上面定义的客户端动作,使用户可以访问该仪表盘。
- 菜单项 (
对于一些简单的“仪表盘”概念,有时也可能通过组合已有的视图类型(如图表、透视表、看板)并使用“收藏夹”功能,或者通过 Studio 创建一个包含多个图表和 KPI 的特定表单视图来实现。然而,功能完善的、交互式的仪表盘通常采用客户端动作和 OWL 组件的方式。
- 关键元素与属性 (对于基于 OWL 的仪表盘):
ir.actions.client
记录:name
:仪表盘的名称。tag
:在 JavaScript 动作注册表中注册的唯一标识符,用于调用相应的 OWL 组件 。res_model
/params
:可选,可以传递模型信息或参数给 JS 组件。
- OWL 组件 (JavaScript):
- 使用
useState
管理响应式状态。 - 使用
useService("orm")
或rpc
服务进行数据获取 。 - 可能包含子组件(如图表组件、KPI 卡片组件)。
- 生命周期钩子(如
onWillStart
,onMounted
)用于数据加载和 DOM 操作。
- 使用
- QWeb 模板 (XML):
- 使用
t-name
定义模板。 - 使用 QWeb 指令 (
t-foreach
,t-if
,t-esc
,t-call
,t-ref
等) 动态渲染内容。 - 可以集成其他 Odoo 核心 OWL 组件或第三方图表库。
- 使用
- Odoo 18 的预期改进:
Odoo 18 预计将“改进仪表盘创建的用户体验” ,这可能意味着更简化的工具或更强大的内置组件来帮助开发者和用户构建仪表盘。同时,新增了“将仪表盘添加到收藏夹”的功能 ,提升了用户对常用仪表盘的访问便捷性。
后端仪表盘在 Odoo 中越来越多地依赖 OWL 组件来实现,这为创建高度灵活和交互丰富的用户界面提供了可能。与传统的、主要由 XML 定义的视图不同,仪表盘的开发通常需要开发者同时掌握 XML(用于 QWeb 模板)、JavaScript (OWL 组件逻辑) 和 Python (用于后端数据供给方法)。这种转变使得仪表盘能够集成更复杂的图表、实时数据更新以及更流畅的用户交互,从而提供更接近独立商业智能应用的用户体验。
2.10. QWeb 视图(用于报表和网站页面)
- 用途与使用场景:
QWeb 是 Odoo 的主要模板引擎,它基于 XML 设计,主要用于生成 HTML 片段和页面 。QWeb 视图广泛应用于两个主要领域:
-
- 报表(Reports):生成动态的 PDF 报表(通过 wkhtmltopdf 将 QWeb 渲染的 HTML 转换为 PDF)和 HTML 报表,如发票、销售订单、库存报告等 。
- 网站和门户页面(Website/Portal Pages):构建 Odoo 网站模块中的前端页面,包括电子商务页面、博客、论坛、客户门户等 。
- XML 结构:
QWeb 视图的本质是标准的 XML/HTML 结构,嵌入了 QWeb 特有的指令(以 t-
为前缀的属性或元素)来实现动态内容渲染 。
-
- 对于报表:通常定义在一个
<template id="unique_template_id">
元素内。报表模板常通过t-call="web.html_container"
和t-call="web.external_layout"
来包含标准的报表头部、尾部和样式 。 - 对于网站页面:也使用
<template id="unique_template_id">
,但其内容直接是构成页面的 HTML 和 QWeb 指令。网站页面模板可以通过inherit_id
继承和修改其他模板 。
- 对于报表:通常定义在一个
XML 代码示例 (报表):
<record id="action_report_my_custom_document" model="ir.actions.report">
<field name="name">我的自定义文档</field>
<field name="model">my.custom.model</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">my_module.report_my_custom_document_template</field>
<field name="report_file">my_module.report_my_custom_document_template</field>
<field name="binding_model_id" ref="model_my_custom_model"/>
<field name="binding_type">report</field>
</record>
<template id="my_module.report_my_custom_document_template">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc"> <t t-call="web.external_layout">
<div class="page">
<h2>报表标题: <span t-field="doc.name"/></h2>
<p>这是一个关于 <t t-esc="doc.name"/> 的示例文档。</p>
<p>创建日期: <span t-esc="context_timestamp(datetime.datetime.now()).strftime('%Y-%m-%d %H:%M')"/></p>
<h3>明细行:</h3>
<table class="table table-sm o_main_table">
<thead>
<tr>
<th>描述</th>
<th class="text-end">数量</th>
<th class="text-end">单价</th>
<th class="text-end">小计</th>
</tr>
</thead>
<tbody>
<t t-foreach="doc.line_ids" t-as="line">
<tr>
<td><span t-field="line.description"/></td>
<td class="text-end"><span t-field="line.quantity"/></td>
<td class="text-end"><span t-field="line.price_unit" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/></td>
<td class="text-end"><span t-field="line.price_subtotal" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/></td>
</tr>
</t>
</tbody>
</table>
<p class="text-end"><strong>总计: <span t-field="doc.amount_total" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/></strong></p>
</div>
</t>
</t>
</t>
</template>
XML 代码示例 (网站页面片段):
<template id="my_website_snippet" name="我的网站片段">
<section class="pt32 pb32">
<div class="container">
<div class="row">
<div class="col-lg-12 text-center">
<h2 t-if="editable">在此处编辑标题</h2>
<p t-field="res_company.partner_id.website_description"/>
<t t-foreach="blog_posts" t-as="post">
<div class="mt16">
<h3><a t-attf-href="/blog/#{post.blog_id.id}/post/#{post.id}"><t t-esc="post.name"/></a></h3>
<p><t t-raw="post.teaser"/></p>
</div>
</t>
</div>
</div>
</div>
</section>
</template>
- 关键 QWeb 指令与属性:
t-name="template_name"
:定义一个模板的名称,用于被t-call
调用。t-call="other_template_name"
:调用另一个 QWeb 模板,并可以传递上下文或在调用块内定义新的上下文变量。t-foreach="collection_expression"
和t-as="variable_name"
:遍历一个集合(如记录集、列表),并将每个元素赋值给variable_name
。t-if="condition_expression"
/t-elif="condition_expression"
/t-else=""
:条件渲染块。t-esc="expression"
:计算表达式并将其结果进行 HTML 转义后输出。用于显示文本内容,防止 XSS 攻击。t-raw="expression"
:计算表达式并将其结果直接输出,不进行 HTML 转义。谨慎使用,仅用于已知安全的 HTML 内容。t-out="expression"
:与t-esc
类似,用于输出表达式结果。t-field="record.field_name"
:专门用于渲染 Odoo 模型的字段值。它会根据字段类型(如日期、货币、关系字段)自动选择合适的格式化方式和小部件。t-options="json_options_dict"
:与t-field
配合使用,传递小部件选项,如{'widget': 'monetary', 'display_currency': record.currency_id}
。
t-set="variable_name" t-value="expression"
或<t t-set="variable_name">value_block</t>
:在当前 QWeb 上下文中定义一个新变量。t-att-attribute_name="expression"
:动态设置 HTML 元素的属性值,例如t-att-class="'active' if condition else ''"
。t-attf-attribute_name="formatted_string_with_#{expressions}"
:动态设置 HTML 元素的属性值,允许在字符串中内嵌表达式,例如t-attf-href="/shop/product/#{product.id}"
。t-call-assets="module.asset_bundle_name"
:在网站模板中用于加载 CSS/JS 资源包。- 报表特定上下文:在报表模板中,通常可以访问
docs
(当前报表处理的记录集),doc_model
(模型名称),user
(当前用户记录),res_company
(当前公司记录),time
(Python time 模块),context_timestamp
(用于格式化带时区的时间戳) 等变量 。
QWeb 视图与其他后端视图(如表单、列表)的关键区别在于其主要目标是生成 HTML 输出,而非直接渲染可交互的 Odoo 后端 UI 组件。t-field
指令是 QWeb 中处理模型字段的一大亮点,它能够智能地根据字段类型和用户的区域设置(如日期格式、数字格式、货币符号)进行恰当的渲染,确保了数据表示的一致性和专业性。这对于生成符合本地化要求的报表和网站内容尤为重要。对于需要翻译的报表,通常会定义主模板和可翻译文档模板,通过 t-lang
属性传递合作伙伴的语言代码,实现报表内容的动态翻译 。
2.11. 活动视图(Activity View)
- 用途与使用场景:
活动视图专门用于管理和调度与记录相关的活动(如待办事项、邮件、电话、会议等)。它帮助用户清晰地追踪各项任务的进展,确保不会遗漏重要的后续行动。活动视图通常与 Odoo 的活动调度机制(mail.activity.mixin
)集成。
- XML 结构:
活动视图的根标签是 <activity>
。其内部包含 <templates>
标签,其中定义了一个名为 activity-box
的 QWeb 模板,该模板规定了每条活动记录在视图中的显示样式和内容。活动视图也可以直接包含 <field>
声明,以确保这些字段的数据被获取并能在 activity-box
模板中使用。
XML 代码示例:
<record id="view_crm_lead_activity" model="ir.ui.view">
<field name="name">crm.lead.view.activity</field>
<field name="model">crm.lead</field>
<field name="arch" type="xml">
<activity string="销售线索活动">
<field name="name"/>
<field name="partner_id"/>
<field name="expected_revenue"/>
<field name="user_id"/>
<templates>
<div t-name="activity-box"> <div class="d-flex align-items-start">
<field name="user_id" widget="many2one_avatar_user"/> <div class="ms-2 flex-grow-1">
<div class="o_activity_record_title mb-1">
<field name="name" class="fw-bold me-2"/> <field name="partner_id" class="text-muted"/>
</div>
<div>
<strong><t t-esc="activity.summary or '无摘要'"/></strong>
</div>
<div class="text-muted">
截止日期: <field name="activity_date_deadline" widget="date"/>
类型: <field name="activity_type_id"/>
</div>
<div t-if="record.expected_revenue.value"> 预期收入: <field name="expected_revenue" widget="monetary" options="{'currency_field': 'company_currency'}"/>
</div>
</div>
</div>
</div>
</templates>
</activity>
</field>
</record>
注:在实际的 activity-box
QWeb 模板中,来自 mail.activity
模型的字段(如活动摘要、截止日期、活动类型)通常会通过一个名为 activity
的上下文变量(或类似名称)来访问,例如 activity.summary
。而直接在 <activity>
标签下声明的 <field name="..."/>
是为了从主模型(这里是 crm.lead
)中获取数据以在活动条目中显示。
- 关键元素与属性:
<activity>
(根元素):string
:视图的描述性标题 。
<field>
(在<activity>
标签下,<templates>
之外):name
:声明主模型中的字段名。这些字段的值将被获取,并可以在activity-box
模板中通过record.field_name.value
或类似方式访问,用于展示与活动相关的记录的上下文信息 。
<templates>
:- 包含一个或多个 QWeb 模板。
<div t-name="activity-box">
(或类似命名的根模板元素):定义了单个活动条目在视图中的 HTML 结构和内容 。- 可以使用 QWeb 指令 (
t-if
,t-esc
,t-field
等) 和 HTML 标签。 - 可以显示来自主模型的字段(通过
record
对象)和来自活动记录本身的字段(通常通过activity
对象,由视图的 JavaScript 控制器提供)。
- 可以使用 QWeb 指令 (
活动视图的独特之处在于它结合了特定视图类型的声明 (<activity>
) 与 QWeb 模板的灵活性,用于定制每个活动条目的具体呈现方式。在 <activity>
标签下直接声明的 <field>
元素非常重要,因为它们定义了哪些来自主记录的数据可以被 activity-box
模板所用,从而丰富活动条目的上下文信息。例如,在销售线索的活动视图中,除了显示活动本身的摘要和截止日期外,可能还需要显示线索的名称、客户名称或预期收入等信息,这些信息就需要通过这种方式声明。
2.12. 地图视图(Map View)
- 用途与使用场景:
地图视图用于在交互式地理地图上展示带有位置信息的记录,例如客户地址、服务点、活动地点等 。它有助于用户直观地分析数据的地理分布、规划路线或理解空间关系。此视图是 Odoo 企业版的功能 。
- XML 结构:
地图视图的根标签是 <map>
。
XML 代码示例:
<record id="view_partner_map" model="ir.ui.view">
<field name="name">res.partner.view.map</field>
<field name="model">res.partner</field>
<field name="arch" type="xml">
<map string="合作伙伴地图"
res_partner="self" routing="true" hide_name="false"
hide_address="false"
panel_title="合作伙伴列表"
limit="200"
default_order="name asc">
<field name="name" string="名称"/>
<field name="street" string="街道"/>
<field name="city" string="城市"/>
<field name="country_id" string="国家"/>
<field name="phone" string="电话"/>
<field name="website" string="网站" widget="url"/>
</map>
</field>
</record>
- 关键元素与属性:
<map>
(根元素):string
:视图的标题。res_partner
:必需或高度推荐。指定一个字段名,该字段是当前模型中与res.partner
模型的 many2one 关系字段。地图视图将使用此伙伴记录的地址信息进行地理编码并在地图上定位。如果当前模型本身就是res.partner
或其派生模型,则可以设为self
。routing
:布尔值 ('1'
或true
/'0'
或false
)。如果为true
,则在选择了多个地图标记点时,会尝试显示它们之间的最短路径。这通常需要配置地图服务提供商(如 MapBox)的 API 密钥,并且至少有两个已定位的记录 。hide_name
:布尔值。如果为true
,则在地图标记点的弹窗中隐藏记录的名称,默认为false
。hide_address
:布尔值。如果为true
,则在地图标记点的弹窗中隐藏记录的地址,默认为false
。hide_title
:布尔值。如果为true
,则隐藏地图视图侧边栏(如果有)中标记点列表的标题,默认为false
。panel_title
:可选字符串,自定义侧边栏标记点列表的标题 。limit
:获取并显示在地图上的最大记录数,默认为 80 。default_order
:覆盖模型默认排序的排序表达式,用于确定标记点列表的顺序 。
<field>
(在<map>
标签下):- 定义当用户点击地图上的标记点时,在弹出的信息窗口中显示的字段。每个
<field>
元素代表信息窗口中的一行 。 name
:模型中字段的名称,必需 。string
:可选,在字段值之前显示的标签。如果未提供,则不显示标签或使用模型字段的默认标签 。widget
:可选,指定用于显示字段值的小部件,例如url
,email
。
- 定义当用户点击地图上的标记点时,在弹出的信息窗口中显示的字段。每个
地图视图的功能在很大程度上依赖于与外部地理位置数据平台的集成。默认情况下,Odoo 可能使用 OpenStreetMap 等免费服务进行地图瓦片和地理编码。为了获得更高级的功能(如精确的路线规划或更高的地理编码请求限制),通常需要配置商业地图服务提供商(如 MapBox)的 API 密钥 。res_partner
属性是地图视图的核心,它指明了从哪个 res.partner
记录(或通过哪个关联字段找到的 res.partner
记录)获取地址信息,这些地址信息随后被用于在地图上定位标记点。因此,确保关联的伙伴记录拥有准确且可被地理编码的地址数据是地图视图正常工作的先决条件。
2.13. 层级视图(Hierarchy View)
- 用途与使用场景:
层级视图用于展示具有父子关系(parent-child relationship)的记录,通常以组织结构图或树状图的形式呈现 。这种视图非常适合可视化如公司部门结构、员工汇报关系、产品分类层级、物料清单 (BOM) 结构等层级化数据。根据 Webkul 的信息,层级视图是 Odoo 18 知识库应用中一个预期的新特性,旨在提供更有组织的文档结构,方便用户管理和导航内容 。
- XML 结构:
层级视图的根标签是 <hierarchy>
。一个关键属性是 child_field
,它定义了模型中用于表示子记录的 one2many 字段。通常还会使用 js_class
属性来指定一个 JavaScript 类,负责该视图的特定渲染逻辑和交互行为。视图内部可以包含 <templates>
标签,用于定义层级中每个节点的显示样式。
XML 代码示例 :
<record id="view_hr_employee_hierarchy" model="ir.ui.view">
<field name="name">hr.employee.view.hierarchy</field>
<field name="model">hr.employee</field>
<field name="arch" type="xml">
<hierarchy child_field="child_ids" js_class="hr_employee_hierarchy" draggable="1"> <field name="name"/>
<field name="job_title"/>
<field name="image_128" widget="image"/>
<field name="parent_id"/> <templates>
<t t-name="hierarchy-box"> <div class="o_hierarchy_node text-center">
<div class="o_hierarchy_node_image mb-2">
<field name="image_128" widget="image" class="oe_avatar" style="width: 80px; height: 80px;"/>
</div>
<div class="o_hierarchy_node_content">
<div class="fw-bold"><field name="name"/></div>
<div class="text-muted" t-if="record.job_title.value"><field name="job_title"/></div>
</div>
</div>
</t>
</templates>
</hierarchy>
</field>
</record>
- 关键元素与属性:
<hierarchy>
(根元素):child_field
:必需,指定模型中代表子记录集合的 one2many 字段的名称。这是构建层级结构的核心 。js_class
:可选,指定一个 JavaScript (OWL) 组件的名称,用于自定义层级视图的渲染和交互逻辑。例如,hr_employee_hierarchy
可能是一个专门为员工组织结构图设计的 JS 类 。draggable
:布尔值 ('1'
或'0'
),指示是否允许用户通过拖拽来改变记录在层级中的位置(即改变其父记录),默认为False
。string
:视图的标题。
<field>
(在<hierarchy>
标签下,<templates>
之外):- 声明模型中的字段,这些字段的数据将被获取并可以在
hierarchy-box
QWeb 模板中使用。
- 声明模型中的字段,这些字段的数据将被获取并可以在
<templates>
:- 包含一个或多个 QWeb 模板。
<t t-name="hierarchy-box">
(或类似命名的根模板元素):定义了层级结构中单个节点的 HTML 结构和内容 。- 可以使用 QWeb 指令 (
t-if
,t-esc
,t-field
等) 和 HTML 标签。 - 可以访问通过
<field>
声明的字段值 (通常通过record.field_name.value
或record.field_name.raw_value
)。
- 可以使用 QWeb 指令 (
层级视图专门用于可视化具有内在层级关系的数据。child_field
属性是其定义的基石,它告诉视图如何从一个父记录导航到其直接子记录。js_class
属性的存在表明,像看板视图和仪表盘视图一样,层级视图的复杂渲染(如树状布局算法、连接线的绘制)和交互(如节点的展开/折叠、拖拽操作)很可能由客户端的 JavaScript 代码处理。这使得开发者能够实现比简单列表或表单更丰富的可视化效果,而无需从头编写所有复杂的 JS 逻辑,因为 Odoo 框架或特定模块(如 hr_org_chart
)可能已经提供了基础的 JS 组件。Odoo 18 中对知识库应用引入层级视图的预期 ,也反映了对更优内容组织和导航方式的需求。同时,Odoo ORM 本身也支持高效的层级查询,例如通过 parent_path
字段和 child_of
/ parent_of
域操作符 ,这为层级视图的后端数据处理提供了性能保障。
2.14. 群组视图(Cohort View)
- 用途与使用场景:
群组视图(Cohort View)是一种专门用于分析记录生命周期随时间变化情况的工具 。它通常用于追踪特定时间段内开始的“群组”(cohorts)在后续时间周期内的行为,例如用户留存率、订阅续订率、客户流失分析等。此视图是 Odoo 企业版的功能 。
- XML 结构:
群组视图的根标签是 <cohort>
。
XML 代码示例 :
<record id="view_subscription_cohort" model="ir.ui.view">
<field name="name">sale.subscription.cohort</field>
<field name="model">sale.subscription</field>
<field name="arch" type="xml">
<cohort string="订阅留存分析"
date_start="date_start" date_stop="date_close" interval="month" mode="retention" timeline="forward" measure="recurring_monthly" sample="1">
<field name="stage_id" invisible="1"/> </cohort>
</field>
</record>
- 关键元素与属性:
<cohort>
(根元素):string
:视图的标题,必需 。date_start
:必需,模型中代表记录(群组成员)开始日期的字段名(date 或 datetime 类型)。这是定义群组的依据。date_stop
:必需,模型中代表记录结束日期或“流失”日期的字段名(date 或 datetime 类型)。用于计算在特定时间间隔后,群组中还有多少成员保持活跃。interval
:群组分析的时间间隔单位,可选值为day
,week
,month
,year
。默认为month
。mode
:分析的模式,可选值为retention
(计算留存百分比或数量,默认) 或churn
(计算流失百分比或数量) 。timeline
:时间线的方向,可选值为forward
(从群组开始日期向前推移,默认) 或backward
(从群组结束日期向后回溯) 。measure
:可选,一个可聚合的数字字段名。如果提供,单元格中将显示此字段的聚合值(通常是总和或平均值,取决于字段类型和默认聚合行为);如果未提供,单元格通常显示群组中在该时间点仍然活跃的记录数量或百分比 。disable_linking
:设为'1'
(或True
) 时,单元格中的值将不可点击以跳转到对应的列表视图,默认为False
。sample
:布尔值,当没有记录时,是否显示示例群组图,默认为False
。
<field>
(在<cohort>
标签下):- 的文档中提到
<field>
元素是可选的,并列出了一些通用属性如name
,string
,invisible
,widget
。然而,它没有详细说明这些字段在群组视图的具体呈现方式或逻辑作用。它们可能用于提供额外的上下文信息,或者在与 Studio 等工具结合使用时有特定用途。在基本的群组视图定义中,核心维度由<cohort>
标签的属性(如date_start
,date_stop
,measure
)决定。
- 的文档中提到
群组视图是一种高度专业化的分析工具,它自动处理了定义群组、按时间间隔追踪群组行为以及计算相关指标(如留存率)的复杂逻辑。date_start
字段用于将记录划分到不同的群组(例如,所有在一月份开始订阅的用户属于同一群组),而 date_stop
字段则用于判断在后续的每个 interval
(如月份)结束时,该群组的成员是否仍然“活跃”或已“流失”。measure
属性允许分析人员追踪除了计数之外的其他量化指标,例如每个群组在不同时期的平均消费额或总收入贡献。这种视图对于理解产品粘性、评估营销活动效果或预测客户生命周期价值非常有用。
2.15. 网格视图(Grid View)
- 用途与使用场景:
网格视图(Grid View)以矩阵或电子表格的形式展示数据,允许用户在一个二维的网格中查看和批量编辑记录 。它特别适用于需要同时考虑两个维度进行规划、分配或数据录入的场景,例如资源(如员工、设备)在一段时间内(如日期、周、月)的分配情况(如工时、任务),或者产品属性组合的库存或价格管理。此视图是 Odoo 企业版的功能 。
- XML 结构:
网格视图的根标签是 <grid>
。其内部包含用于定义行维度、列维度和单元格度量值的 <field>
元素。列维度字段还可以包含 <range>
子元素来定义可切换的时间范围。
XML 代码示例 :
<record id="view_resource_timesheet_grid" model="ir.ui.view">
<field name="name">resource.timesheet.view.grid</field>
<field name="model">account.analytic.line</field> <field name="arch" type="xml">
<grid string="工时表网格"
adjustment="object"
adjust_name="action_adjust_grid_cell_value" create_inline="true" hide_line_total="false"
barchart_total="true">
<field name="employee_id" type="row"/> <field name="project_id" type="row"/> <field name="date" type="col"> <range name="week" string="本周" span="week" step="day"/>
<range name="month" string="本月" span="month" step="day"/>
<range name="year" string="本年" span="year" step="month"/>
</field>
<field name="unit_amount" type="measure" widget="float_time"/> <button name="action_submit_timesheets" string="提交工时表" type="object" class="btn-primary"/>
</grid>
</field>
</record>
- 关键元素与属性:
<grid>
(根元素):string
:视图标题,必需 。create
/edit
/delete
:布尔值,控制是否允许创建、编辑、删除记录(通过此视图的交互),默认为True
。adjustment
:可选,指定当单元格的值被修改时如何处理。可以是'object'
(调用一个 Python 方法) 或'action'
(执行一个ir.actions
动作) 。adjust_name
:可选,如果adjustment
被设置,则此属性指定要调用的方法名或动作的 XML ID 。create_inline
:布尔值,如果为True
,则在网格底部显示一个“添加一行”的按钮,允许用户直接在网格中创建新记录,默认为False
。hide_line_total
/hide_column_total
:布尔值,是否隐藏行合计或列合计,默认为False
。barchart_total
:布尔值,是否在网格底部显示一个基于列合计的条形图,默认为False
。display_empty
:布尔值,当没有数据时是否仍然显示网格结构(空的),默认为False
。
<field type="row">
(行维度):name
:模型中用作行分组依据的字段名。可以有多个,形成层级行 。widget
:可选,用于自定义行标签的显示。
<field type="col">
(列维度):name
:模型中用作列分组依据的字段名(通常是日期或选择类型字段)。- 内部可以包含一个或多个
<range>
元素。 <range>
(列范围定义):name
:范围的技术名称,必需 。string
:显示给用户的范围标签(例如“周”、“月”),必需 。span
:范围的跨度单位,如day
,week
,month
,year
,必需 。step
:范围内每个单元格的步长单位,如hour
,day
,week
,必需 。
<field type="measure">
(度量值):name
:模型中代表单元格值的字段名(通常是数字类型)。widget
:可选,用于自定义单元格值的显示和编辑,例如float_time
,monetary
,progressbar
。operator
:聚合函数,如sum
(默认)。
<button>
(在<grid>
标签下):- 定义在网格视图头部(控制面板区域)显示的按钮,用于执行与网格数据相关的操作 。
- 属性包括
name
,string
,type
(object
或action
),context
等。
网格视图的核心在于其能够将数据清晰地映射到一个二维矩阵中,其中行和列由指定的维度字段定义,而交叉点(单元格)则显示或允许编辑一个度量字段的值。<field type="col">
内部的 <range>
元素特别强大,它允许用户在不同的时间粒度(如按天、按周、按月)之间切换列的显示,这对于规划类应用非常有用。例如,在资源调度场景中,行可能是员工,列可能是日期(通过 <range>
定义为天、周或月),单元格则可能是分配的工时。adjustment
和 adjust_name
属性,结合服务器端的 adjust_grid
方法,使得单元格的修改可以直接触发后端业务逻辑,实现了真正的交互式编辑体验。在 Odoo 18 中使用“变体网格条目”(Variant Grid Entry)来快速添加多种产品变体到销售订单的功能 ,这虽然是一个特定的应用场景(产品配置器的一部分),但也体现了网格化界面在处理多选项组合时的效率。
2.16. 设置视图(Settings View)
- 用途与使用场景:
设置视图是 Odoo 中一种特殊用途的表单视图,专门用于展示和配置应用程序或模块的全局设置项 。用户通常通过“配置”菜单下的“设置”选项访问这些视图。它们提供了一个集中、一致的界面来管理模块行为。
- XML 结构:
设置视图本身是基于表单视图 (<form>
) 的,但它通过继承一个基础的设置模型(通常是 res.config.settings
)并在其 arch
中使用特定的结构化标签 <app>
, <block>
, 和 <setting>
来组织设置项 。
XML 代码示例 (为一个自定义模块添加设置):
# class ResConfigSettings(models.TransientModel):
# _inherit = 'res.config.settings'
# module_my_custom_app = fields.Boolean(string="启用我的自定义应用特性")
# my_custom_app_api_key = fields.Char(string="API 密钥", config_parameter='my_custom_app.api_key')
# my_custom_app_default_user_id = fields.Many2one('res.users', string="默认用户", config_parameter='my_custom_app.default_user_id')
<record id="view_my_custom_app_config_settings" model="ir.ui.view">
<field name="name">my.custom.app.config.settings.form</field>
<field name="model">res.config.settings</field>
<field name="priority">100</field> <field name="inherit_id" ref="base.res_config_settings_view_form"/> <field name="arch" type="xml">
<xpath expr="//div[hasclass('settings')]" position="inside"> <app string="我的自定义应用" name="my_custom_app" logo="/my_custom_app/static/description/icon.png"
groups="base.group_system"> <block title="主要配置" name="my_custom_app_main_config"
help="这里配置我的自定义应用的核心参数。">
<setting string="启用核心特性" help="勾选以激活应用的核心功能。"
documentation="/my_custom_app/static/doc/core_feature_guide.html">
<field name="module_my_custom_app"/> <div class="text-muted" invisible="module_my_custom_app == False">
核心特性已启用!现在您可以配置 API 密钥。
</div>
</setting>
<setting string="API 设置" company_dependent="0" invisible="module_my_custom_app == False">
<label for="my_custom_app_api_key" string="API 密钥"/>
<field name="my_custom_app_api_key" class="oe_inline" placeholder="输入您的 API 密钥..."/>
<button name="action_test_api_connection" string="测试连接" type="object" class="btn-link"/>
</setting>
</block>
<block title="高级选项" name="my_custom_app_advanced_options">
<setting string="默认负责人">
<field name="my_custom_app_default_user_id" domain="[('share', '=', False)]"/>
</setting>
</block>
</app>
</xpath>
</field>
</record>
- 关键元素与属性:
- 模型继承:设置视图通常关联到一个继承自
res.config.settings
的瞬态模型 (TransientModel)。字段可以直接定义在这个瞬态模型上,并通过config_parameter
属性将其值存储到ir.config_parameter
系统参数中,或者通过related
属性关联到res.company
的字段(用于公司级别设置),或者通过module_{module_name}
形式的布尔字段来控制模块的安装/卸载(尽管这种方式更多是历史用法,现在推荐通过implied_ids
等)。 <app>
:- 在设置界面的侧边栏创建一个应用入口,并作为设置项的顶层容器 。
string
:应用的显示名称,必需 。name
:应用的技术名称(通常是模块名),必需 。logo
:可选,指向应用图标的相对路径(例如/module_name/static/description/icon.png
)。groups
:可选,限制哪些用户组可以看到此应用设置部分 。invisible
:可选,动态隐藏整个应用设置部分 。
<block>
:- 在
<app>
内部组织一组相关的设置项 。 title
:块的标题,可选,支持搜索 。help
:块的描述文本,可选,支持搜索 。groups
/invisible
:与<app>
中的同名属性类似,但作用于块级别 。
- 在
<setting>
:- 声明一个具体的设置项 。
string
:设置项的标签,如果未提供,则通常取其内部第一个<field>
的标签 。help
:显示在标签下方的详细描述文本 。documentation
:可选,指向相关文档页面的路径 。company_dependent
:设为'1'
时,表示此设置是公司特定的(通常意味着其值存储在res.company
模型上,并通过related
字段链接到res.config.settings
中的字段),默认为'0'
(非公司特定) 。groups
/invisible
:与<app>
中的同名属性类似,但作用于单个设置项级别 。- 内部通常包含一个或多个
<field>
元素来显示和编辑设置值,也可以包含其他 HTML 元素(如<div>
,<button>
)来提供更丰富的交互或信息。第一个<field>
通常被视为主设置字段。
- 模型继承:设置视图通常关联到一个继承自
设置视图通过这种结构化的方式 (<app>
, <block>
, <setting>
),为用户提供了一个统一且易于导航的配置界面。开发者通过继承 res.config.settings
模型并扩展其表单视图,可以方便地为自己的模块添加配置选项。使用 config_parameter
属性的字段会自动将设置值保存到系统的 ir.config_parameter
表中,这是一种轻量级的持久化配置数据的方式。对于需要区分公司的设置,则通常将 res.config.settings
中的字段通过 related
属性关联到 res.company
上的相应字段,并标记 company_dependent="1"
。这种设计确保了 Odoo 各个应用设置界面的一致性和易用性。
2.17. 其他可能的新增或重要视图类型
Odoo 的视图系统在不断发展,每个主要版本都可能带来新的视图类型或对现有视图的重大增强。
- 预期的新特性:
- 知识库应用的层级视图 (Hierarchy View):已在 2.13 节中详细介绍,这是 Odoo 18 中一个备受期待的新视图类型,旨在改善知识库等层级内容的组织和导航 。
- 电子表格与仪表盘应用的新图表与选项:Odoo 18 预计会扩展图表功能,提供更多类型和自定义选项,这可能会影响图表视图或仪表盘组件的定义和使用方式 。
- 制造业主生产计划 (MPS) 的 UI 改进:提及了 MPS 的 UI Revamp ,这可能涉及到专门的视图或对现有视图(如列表、表单、甘特图)的深度定制以适应 MPS 的复杂需求。
- 持续关注官方文档:
由于 Odoo 的开发迭代速度较快,最新的视图类型和特性通常会在官方文档的发布说明、开发者文档或特定应用文档中更新。建议开发者持续关注 Odoo 官方渠道(如 Odoo.com/documentation, Odoo Release Notes)以获取关于 Odoo 18 及后续版本视图系统的最新信息。
本指南已尽可能覆盖截至编写时已知的 Odoo 18 核心视图类型。如果 Odoo 18 正式版或后续更新中引入了本指南未包含的其他重要视图类型,其定义和使用方式也将遵循类似的 XML 结构和原则。