第 11 章:锦上添花

我们的房地产模块现在从业务角度看是合理的。我们创建了 特定的视图,添加了若干 动作按钮 和 约束。然而我们的用户界面仍然有些粗糙。我们想为列表视图添加一些颜色,并让某些字段和按钮按条件消失。例如,当房产已售或已取消时,’Sold’ 和 ‘Cancel’ 按钮应该消失,因为此时已不再允许更改状态。

本章只涵盖视图功能中很小的一部分。不要犹豫,请阅读参考文档以获得更完整的概览。

参考:与本章相关的文档可在 视图记录 和 视图架构 中找到。

内联视图

注解

目标:本节结束时,房产类型视图中应添加一个特定的房产列表:

内联列表视图

在房地产模块中,我们为房产添加了报价列表。我们只是添加了 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_1 和 field_2 字段。

示例请见 此处。

练习

添加内联列表视图。

  • 将 One2many 字段 property_ids 添加到 estate.property.type 模型。

  • 按照本节 目标 中的示意,在 estate.property.type 表单视图中添加该字段。

部件

参考:本节相关文档请参阅 字段。

注解

目标:本节结束时,应使用特定的小部件显示房产的状态:

状态栏小部件

系统会显示四种状态:新建、收到报价、报价已接受和已售出。

每当我们在模型中添加字段时,(几乎)从不需要操心这些字段在用户界面中会是什么样子。例如,Date 字段会提供一个日期选择器,而 One2many 字段会自动显示为列表。Odoo 会根据字段类型选择合适的“小部件”。

然而,在某些情况下,我们希望字段有特定的显示形式,这可以通过 widget 属性实现。我们之前已经对 tag_ids 字段使用过它,当时使用的是 widget="many2many_tags" 属性。如果没有使用它,那么该字段会显示为列表。

每种字段类型都有一组小部件,可用于精细调整其显示效果。有些小部件还接受额外选项。完整列表请参阅 字段。

练习

使用状态栏小部件。

按照本节 目标 中的示意,使用 statusbar 小部件显示 estate.property 的 state。

提示:一个简单示例可在 此处 找到。

警告

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

在列表视图或表单视图中,每个字段只能添加 一次。不支持多次添加同一字段。

列表顺序

参考:本节相关文档请参阅 型号。

注解

目标:本节结束时,所有列表都应默认按确定性顺序显示。房产类型可以手动排序。

在之前的练习中,我们创建了几个列表视图。然而,我们从未指定记录默认按什么顺序列出。对许多业务场景来说,这一点非常重要。例如,在我们的房地产模块中,我们希望把最高的报价显示在列表顶部。

模型

Odoo 提供了几种设置默认顺序的方法。最常见的方法是在模型中直接定义 _order 属性。这样,检索到的记录将遵循确定性顺序,该顺序在所有视图中都保持一致,包括通过程序搜索记录时。默认情况下不指定顺序,因此记录将按取决于 PostgreSQL 的非确定性顺序检索。

_order 属性接受一个字符串,其中包含用于排序的字段列表。它会被转换为 SQL 中的 order_by 子句。例如:

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 属性指定特定顺序(示例)。

手动

模型排序和视图排序都能在排序记录时提供灵活性,但还有一种情况需要涵盖:手动排序。用户可能希望根据业务逻辑对记录排序。例如,在我们的房地产模块中,我们希望手动对房产类型排序。让最常用的类型出现在列表顶部确实很有用。如果我们的房地产中介主要销售房屋,那么把“房屋”排在“公寓”前面会更方便。

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

练习

添加手动排序。

  • 添加以下字段:

模型

字段

类型

estate.property.type

排序

整数

  • 使用正确的小部件,将 sequence 字段添加到 estate.property.type 列表视图。

提示:示例可参见 模型 和 视图。

属性与选项

详细介绍所有可精细调整视图外观的可用功能会过于繁琐。因此,我们只介绍最常见的那些。

窗体

注解

目标:本节结束时,房产表单视图将具有以下内容:

  • 按钮和字段的条件显示

  • 标签颜色

带装饰的表单视图

在我们的房地产模块中,我们想修改一些字段的行为。例如,我们不希望能够从表单视图创建或编辑房产类型,而是期望在对应的菜单中管理这些类型。我们还希望给标签加上颜色。为了添加这些行为定制,我们可以为若干字段小部件添加 options 属性。

练习

添加小部件选项。

  • 为 property_type_id 字段添加合适的选项,以阻止从房产表单视图创建和编辑房产类型。更多信息请查看 Many2one 小部件文档。

  • 添加以下字段:

模型

字段

类型

estate.property.tag

颜色

整数

然后为 tag_ids 字段添加合适的选项,以便在标签上添加颜色选择器。更多信息请查看 FieldMany2ManyTags 小部件文档。

在 第 5 章:终于有点界面可以玩了 中,我们了解到保留字段用于特定行为。例如,active 字段用于自动过滤掉非活动记录。我们也将 state 添加为保留字段。现在该用上它了!state 字段可以与视图中的 invisible 属性结合使用,从而有条件地显示按钮。

练习

添加按钮的条件显示。

使用 invisible 属性,按照本节 目标 中的示意有条件地显示头部按钮(注意当状态改变时,“已售出”和“取消”按钮会如何变化)。

提示:不妨在 Odoo 的 XML 文件中搜索 invisible= 来寻找一些示例。

更一般地,可以根据其他字段的值,让字段变为 invisible、readonly 或 required。请注意,invisible 也可以应用于视图中的其他元素,例如 button 或 group。

invisible、readonly 和 required 可以以任意 Python 表达式作为值。该表达式给出该属性生效的条件。例如:

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

这意味着当 is_partner 为 False 时,description 字段不可见。

练习

使用 invisible。

  • 在没有花园时,隐藏 estate.property 表单视图中的花园面积和朝向字段。

  • 报价状态设置后,隐藏“接受”和“拒绝”按钮。

  • 当房产状态为“报价已接受”“已售出”或“已取消”时,不允许添加报价。为此请使用 readonly 属性。

警告

在视图中使用(条件式) readonly 属性可以帮助防止数据录入错误,但请记住,它并不提供任何级别的安全性!服务器端不会进行任何检查,因此始终可以通过 RPC 调用写入该字段。

列表

注解

目标:本节结束时,房产和报价的列表视图应带有颜色装饰。此外,报价和标签将可以直接在列表中编辑,可用日期默认隐藏。

带装饰和可选字段的列表视图 可编辑列表

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

练习

让列表视图可编辑。

让 estate.property.offer 和 estate.property.tag 列表视图可编辑。

另一方面,当模型有很多字段时,很容易忍不住在列表视图中添加过多字段,导致列表不再清晰。另一种方法是添加这些字段,但让它们可选择隐藏。这可以通过 optional 属性实现。

练习

将字段设为可选。

将 estate.property 列表视图中的 date_availability 字段设为可选并默认隐藏。

最后,颜色代码有助于在视觉上突出显示记录。例如,在房地产模块中,我们希望将已拒绝的报价显示为红色,将已接受的报价显示为绿色。这可以通过 decoration-{$name} 属性实现(完整列表请参阅 字段):

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

is_partner 为 True 的记录将显示为绿色。

练习

添加一些装饰。

在 estate.property 列表视图中:

  • 收到报价的房产显示为绿色

  • 报价已接受的房产显示为绿色加粗

  • 已售出的房产以浅色显示

在 estate.property.offer 列表视图中:

  • 已拒绝的报价显示为红色

  • 已接受的报价显示为绿色

  • 状态字段不应再可见

提示:

  • 请记住,属性中用到的 所有 字段都必须位于视图中!

  • 如果你想测试“收到报价”和“报价已接受”状态的颜色,请在表单视图中添加该字段并手动修改(稍后我们会实现相应的业务逻辑)。

统计按钮

注解

目标:本节结束时,房产类型表单视图上将有一个统计按钮,点击后会显示与该类型房产相关的所有报价列表。

统计按钮

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

在教程的这个阶段,我们已经见过完成此任务所需的大部分概念。然而,解决方案并不唯一,如果不知道从哪里开始,可能仍会感到困惑。我们将在练习中给出逐步的解决方案。在 Odoo 代码库中搜索 oe_stat_button 来查找一些示例通常也会很有帮助。

下面的练习可能比之前的稍难一些,因为它假定你能够自己在源代码中查找示例。如果你卡住了,附近大概有人能帮你 ;-)

该练习引入了 关联字段 的概念。最简单的理解方式是把它看作计算字段的一种特例。以下 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_ids 和 offer_count 添加到视图。下一步是在点击统计按钮时显示该列表。

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

此时,点击统计按钮应该会显示所有报价。我们还需要对这些报价进行过滤。

  • 在 estate.property.offer 动作上添加一个域,将 property_type_id 定义为等于 active_id (= 当前记录,这里有一个示例)

看起来不错吧?如果还不完美,别担心,下一章 并不需要统计按钮 ;-)