跳至内容

老赵解读Odoo 18开发教程11:完善交互体验

2026年8月15日
老赵解读Odoo 18开发教程11:完善交互体验

原文:Odoo 18 官方开发教程“Server framework 101”第 11 章。本文尽量保留原文结构、代码、练习与图片,仅进行中文翻译,并在文末增加“老赵解读”。

第11章:添加糖粉


从商业角度来看,我们的房地产模块现在变得有意义。我们创造了
具体意见,添加了几个
动作按钮
限制条件。然而我们的用户界面仍然有点
粗糙。我们想向列表视图添加一些颜色,并有条件地制作一些字段和按钮
消失。例如,当房产时,“已售出”和“取消”按钮应该消失
已出售或取消,因为此时不再允许更改状态。

本章涵盖了可以在视图中执行的操作的一小部分。不要犹豫
阅读参考文档以获得更完整的概述。

参考:与本章相关的文档可以在
查看记录
查看架构

内联视图

说明

目标:在本节末尾,应将房产的特定列表添加到房产
类型视图:

Inline list view

在房地产模块中,我们为房产添加了报价列表。我们只是添加了字段
offer_ids 具有:

<field name="offer_ids"/>

字段使用 estate.property.offer 的特定视图。在某些情况下我们想要定义
仅在表单视图上下文中使用的特定列表视图。例如,我们想要
显示链接到房产类型的房产列表。但是,我们只想显示 3 个
字段为了清楚起见:名称、预期价格和状态。

为此,我们可以定义 内联 列表视图。内联列表视图直接在内部定义
表单视图。例如:

from odoo import fields, models

class TestModel(models.Model):
    _name = "test_model"
    _description = "Test Model"

    description = fields.Char()
    line_ids = fields.One2many("test_model_line", "model_id")


class TestModelLine(models.Model):
    _name = "test_model_line"
    _description = "Test Model Line"

    model_id = fields.Many2one("test_model")
    field_1 = fields.Char()
    field_2 = fields.Char()
    field_3 = fields.Char()
<form>
    <field name="description"/>
    <field name="line_ids">
        <list>
            <field name="field_1"/>
            <field name="field_2"/>
        </list>
    </field>
</form>

test_model的表单视图中,我们为 test_model_line定义了一个具体的列表视图
与字段 field_1field_2

可以找到一个例子
这里

练习

添加内联列表视图。

  • One2many字段 property_ids 添加到 estate.property.type模型。
  • estate.property.type 表单视图中添加字段,如以下所示 目标 这个的
    部分。

小部件

参考:与本节相关的文档可以在
字段

说明

目标:在本节末尾,应使用 a 显示房产的状态
具体小部件:

Statusbar widget

显示四种状态:新、报价已收到、报价已接受并已售出。

每当我们将字段添加到模型时,我们(几乎)从来不必担心如何
这些字段在用户界面中看起来像这样。例如,提供了日期选择器
对于 Date字段和 One2many字段会自动显示为列表。奥杜
根据字段类型选择正确的“小部件”。

然而,在某些情况下,我们需要字段的特定表示,这可以通过以下方式完成
widget 属性。当我们使用
widget="many2many_tags" 属性。如果我们没有使用它,那么字段将显示为
列表。

每个字段类型都有一组小部件,可用于微调其显示。一些小部件也
采取额外的选择。详细列表可在 字段 中找到。

练习

使用状态栏小部件。

使用 statusbar 小部件将 estate.propertystate 显示为
中描绘的 目标 本节的。

提示:可以找到一个简单的例子
这里

警告

在一个视图中多次出现相同的字段

仅添加字段 一次 到列表或表单视图。多次添加就是
不支持。

列表顺序

参考:与本节相关的文档可以在
模型

说明

目标:在本节末尾,所有列表都应默认以确定性方式显示
订单。房产类型可以手动订购。

在前面的练习中,我们创建了几个列表视图。然而,我们从未指定
默认情况下记录必须按哪个顺序列出。这对于很多企业来说是非常重要的事情
案例。例如,在我们的房地产模块中,我们希望在顶部显示最高的报价
列表。

模型

Odoo 提供了多种设置默认顺序的方法。最常见的方法是定义
_order 属性直接位于模型中。这样,检索到的记录将跟随
确定性顺序在所有视图中都保持一致,包括搜索记录时
以编程方式。默认情况下没有指定顺序,因此记录将是
根据 PostgreSQL 以不确定的顺序检索。

_order 属性采用一个包含字段列表的字符串,该列表将用于排序。
它将转换为 SQL 中的 订单依据 子句。例如:

from odoo import fields, models

class TestModel(models.Model):
    _name = "test_model"
    _description = "Test Model"
    _order = "id desc"

    description = fields.Char()

我们的记录按 id 降序排列,这意味着最高的排在前面。

练习

添加模型订购。

在其对应的模型中定义以下命令:

模型 订单
estate.property 降序 ID
estate.property.offer 降价价格
estate.property.tag 名称
estate.property.type 名称

查看

可以在模型级别订购。这样做的优点是各处的顺序一致
检索记录列表。然而,也可以直接定义特定的顺序
由于 default_order 属性,在视图中
(例子)。

手册

模型和视图排序都在排序记录时提供了灵活性,但仍然存在一种情况
我们需要涵盖: 手动订购。用户可能希望根据业务对记录进行排序
逻辑。例如,在我们的房地产模块中,我们希望手动对房产类型进行排序。
将最常用的类型显示在列表顶部确实很有用。如果我们的房地产
Agency主要销售房屋,“House”出现在“Apartment”之前更方便。

为此,需要将 sequence字段与 handle 小部件结合使用。显然
sequence字段必须是 _order 属性中的第一个字段。

练习

添加手动订购。

  • 添加以下字段:
模型 字段 类型
estate.property.type 序列 Integer
  • 使用正确的小部件将序列添加到 estate.property.type 列表视图中。

提示:您可以在此处找到示例:
模型

查看

属性和选项

详细说明允许微调外观的所有可用功能是令人望而却步的。
视图。因此,我们将坚持使用最常见的。

形式

说明

目标:在本节末尾,房产表单视图将具有:

  • 按钮和字段的条件显示
  • 标签颜色

Form view with sprinkles

在我们的房地产模块中,我们想要修改一些字段的行为。例如,我们不
希望能够从表单视图创建或编辑房产类型。相反,我们期望
在适当的菜单中处理的类型。我们还想给标签指定颜色。为了添加这些
行为自定义,我们可以将 options 属性添加到多个字段小部件。

练习

添加小部件选项。

  • 将适当的选项添加到 property_type_id字段以防止创建和
    从房产表单视图编辑房产类型。看看
    Many2one 小部件文档 了解更多信息。
  • 添加以下字段:
模型 字段 类型
estate.property.tag 颜色 Integer

然后将适当的选项添加到 tag_ids字段以在标签上添加颜色选择器。
看看 FieldMany2ManyTags 小部件文档
了解更多信息。

第 5 章:最后,一些可以使用的 UI中,我们看到保留的字段用于
具体行为。例如 active字段用于自动过滤掉
不活动记录。我们还添加了 state 作为保留的字段。现在是时候使用它了!
state字段可以与视图中的 invisible 属性结合使用来显示
有条件地按钮。

练习

添加按钮的条件显示。

使用 invisible 属性有条件地显示标题按钮,如图所示
在本节的 目标 (请注意状态更改时“已售出”和“取消”按钮如何变化)。

提示:请随时在 Odoo XML 文件中搜索 invisible= 以获取一些示例。

更一般地,可以制作基于字段 invisiblereadonlyrequired
关于其他字段的价值。请注意,invisible 也可以应用于其他元素
诸如 buttongroup 之类的视图。

invisiblereadonlyrequired 可以使用任何 Python 表达式作为值。表达式
给出房产适用的条件.例如:

<form>
    <field name="description" invisible="not is_partner"/>
</form>

这意味着当 is_partnerFalse 时,description字段不可见。

练习

使用 invisible

  • 使花园面积和方向在 estate.property 表单视图中不可见
    没有花园。
  • 设置报价状态后,使“接受”和“拒绝”按钮不可见。
  • 当房产状态为“报价已接受”、“已售出”或
    ‘取消’。为此,请使用 readonly 属性。

警告

在视图中使用(有条件的)readonly 属性有助于防止数据输入
错误,但请记住,它不提供任何级别的安全性!没有检查完成
服务器端,因此始终可以通过 RPC 调用在字段上写入。

列表

说明

目标:在本节末尾,房产和报价列表视图应该有颜色装饰。
此外,报价和标签可直接在列表中编辑,可用日期为
默认隐藏。

List view with decorations and optional field
Editable list

当模型只有几个字段时,直接通过列表编辑记录会很有用
视图而不必打开表单视图。在房地产示例中,无需打开表单视图
添加报价或创建新标签。这可以通过 editable 属性来实现。

练习

使列表视图可编辑。

使 estate.property.offerestate.property.tag 列表视图可编辑。

另一方面,当模型有很多字段时,可能会很容易在字段中添加太多字段
列表视图并使其不清楚。另一种方法是添加字段,但使它们可选
隐藏的。这可以通过 optional 属性来实现。

练习

将字段设为可选。

使 estate.property 列表视图上的字段 date_availability 成为可选并隐藏
默认。

最后,颜色代码有助于在视觉上强调记录。例如,在房地产领域
我们希望将拒绝的报价显示为红色,接受的报价显示为绿色。这是可以实现的
感谢 decoration-{$name} 属性(请参阅 字段 了解更多信息)
完整列表):

<list decoration-success="is_partner==True">
    <field name="name"/>
</list>

其中 is_partnerTrue 的记录将显示为绿色。

练习

添加一些装饰。

estate.property 列表视图上:

  • 收到报价的房产为绿色
  • 房产接受的报价为绿色且粗体
  • 房产出售时静音

estate.property.offer 列表视图上:

  • 拒绝的报价是红色
  • 已接受的报价为绿色
  • 状态不应该再可见

温馨提示:

  • 请记住 全部 字段中使用的属性必须在视图中!
  • 如果要测试“报价Received”和“报价Accepted”状态的颜色,请添加
    字段在表单视图中手动更改它(我们稍后将实现此业务逻辑)。

搜索

参考:与本节相关的文档可以在
搜索搜索默认值

说明

目标:在本节末尾,将默认过滤可用的房产,
并且搜索居住区域会返回面积大于给定值的结果
数量。

Default filters and domains

最后但并非最不重要的一点是,我们希望在搜索时应用一些调整。首先,我们
希望在访问房产时默认应用“可用”过滤器。为了实现这一目标,我们
需要使用 search_default_{$name} 操作上下文,其中 {$name} 是过滤器名称。
这意味着我们可以定义在操作级别默认激活哪些过滤器。

这是一个例子
行动
以其
相应的过滤器

练习

添加默认过滤器。

estate.property 操作中默认选择“可用”过滤器。

我们模块的另一个有用的改进是能够按居住区域进行有效搜索。
在实践中,用户将希望搜索“至少”给定区域的房产。这是不现实的
期望用户想要找到一个精确的居住区域的房产。总是
可以进行自定义搜索,但这很不方便。

搜索视图 <field> 元素可以具有覆盖的 filter_domain
为在给定的字段上进行搜索而生成的域。在给定的域中,
self 表示用户输入的值。在下面的例子中,它是
用于搜索 namedescription字段。

<search string="Test">
    <field name="description" string="Name and description"
           filter_domain="['|', ('name', 'ilike', self), ('description', 'ilike', self)]"/>
</search>

练习

更改居住区搜索。

filter_domain 添加到生活区域,以包括面积等于或的房产
大于给定值。

统计按钮

说明

目标:在本节末尾,房产类型表单视图上将有一个统计按钮
单击时,它会显示与给定类型的房产相关的所有报价的列表。

Stat button

如果您已经使用过 Odoo 中的一些功能模块,您可能已经遇到过“stat
按钮”。这些按钮显示在表单视图的右上角,可以快速访问
链接的文档。在我们的房地产模块中,我们希望有一个到报价的快速链接
与给定的房产类型相关,如 目标 本节的。

在本教程的这一点上,我们已经了解了执行此操作的大部分概念。然而,
没有单一的解决方案,如果您不知道从哪里开始,它仍然会令人困惑。
我们将在练习中描述逐步解决方案。找到一些总是有用的
通过查找 oe_stat_button 在 Odoo 代码库中找到示例。

以下练习可能比前面的练习稍微困难一些,因为它假设您
可以自行在源代码中搜索示例。如果你被困住了,可能有
附近有人可以帮助您;-)

本练习介绍了 相关字段 的概念。最简单的方法
理解它就是将其视为计算的字段的特定情况。以下定义
description字段的:

...

partner_id = fields.Many2one("res.partner", string="Partner")
description = fields.Char(related="partner_id.name")

相当于:

...

partner_id = fields.Many2one("res.partner", string="Partner")
description = fields.Char(compute="_compute_description")

@api.depends("partner_id.name")
def _compute_description(self):
    for record in self:
        record.description = record.partner_id.name

每次更改合作伙伴名称时,都会修改描述。

练习

将统计按钮添加到房产类型。

  • 将字段 property_type_id 添加到 estate.property.offer。我们可以将其定义为
    property_id.property_type_id 上相关的字段并将其设置为已存储。

由于这个字段,报价在创建时将链接到房产类型。您可以添加
将字段复制到报价的列表视图以确保其正常工作。

  • 将字段 offer_ids 添加到 estate.property.type ,这是 One2many 的逆
    上一步中定义的字段。
  • 将字段 offer_count 添加到 estate.property.type。这是一个计算出来的字段才算数
    给定房产类型的报价数量(使用 offer_ids 来执行此操作)。

此时,您已掌握了了解有多少个报价链接到的所有信息
房产类型。如有疑问,请将 offer_idsoffer_count 直接添加到视图中。
下一步是在单击统计按钮时显示列表。

  • estate.property.type 上创建一个指向 estate.property.offer 的统计按钮
    行动。这意味着您应该使用 type="action" 属性(返回到末尾)
    第 9 章:准备好采取行动了吗?(如果您需要复习)。

此时,单击统计按钮应显示所有报价。我们仍然需要过滤掉
报价。

  • estate.property.offer 操作上,添加定义 property_type_id 的域
    等于 active_id (= 当前的记录,
    这是一个例子)

看起来不错吗?如果没有,别担心,下一章 不需要统计按钮;-)

老赵解读

这一章加入的内联视图、状态栏、装饰、默认筛选和统计按钮,看起来是界面“点缀”,其实都在降低用户理解流程和定位信息的成本。

交互优化要围绕业务优先级,而不是追求控件数量。颜色只用于传递明确状态,默认筛选必须符合多数人的工作范围,统计按钮应连接用户下一步最常执行的动作。

老赵解读Odoo 18开发教程10:约束与数据质量