odoo17开发教程(14):Computed Fields And Onchanges

目录

概述:

计算字段Computed Fields

依赖关系

实践:

计算总面积

计算最佳报价。

Inverse Function反函数

实践:计算报价的有效日期。

其他信息

Onchanges

实践:设置花园面积和方向值。

如何使用它们?


概述:

模型之间的关系是任何 Odoo 模块的关键组成部分。任何业务案例的建模都离不开它们。然而,我们可能希望在给定模型内的字段之间建立联系。有时,一个字段的值由其他字段的值决定,有时,我们希望帮助用户输入数据。

计算字段和 onchanges 概念支持这些情况。虽然本章在技术上并不复杂,但这两个概念的语义非常重要。这也是我们第一次编写 Python 逻辑。到目前为止,除了类定义和字段声明之外,我们还没有写过其他东西。

计算字段Computed Fields

参考:有关此主题的文档可在计算字段的文档中找到。

在房地产模块中,我们定义了居住面积和花园面积。因此,将总面积定义为这两个字段的总和是很自然的。为此,我们将使用计算字段的概念,即某个字段的值将根据其他字段的值计算得出。

到目前为止,字段都是直接存储在数据库中或直接从数据库中检索的。字段也可以计算。在这种情况下,字段的值不是从数据库中检索出来的,而是通过调用模型的方法即时计算出来的。

要创建计算字段,请创建一个字段并将其属性 compute 设置为方法名称。计算方法应为 self 中的每条记录设置计算字段的值。

按照惯例,计算方法是私有的,这意味着它们不能从表现层调用,只能从业务层调用(参见第 1 章:架构概述)。私有方法的名称以下划线 _ 开头。

依赖关系

计算字段的值通常取决于计算记录中其他字段的值。ORM 希望开发人员使用装饰器 depends()在计算方法中指定这些依赖关系。只要字段的某些依赖项被修改,ORM 就会使用给定的依赖项来触发字段的重新计算:

from odoo import api, fields, models

class TestComputed(models.Model):
    _name = "test.computed"

    total = fields.Float(compute="_compute_total")
    amount = fields.Float()

    @api.depends("amount")
    def _compute_total(self):
        for record in self:
            record.total = 2.0 * record.amount

📔提示

self是一个集合。

对象 self 是一个记录集,即记录的有序集合。它支持标准的 Python 集合操作,如 len(self) 和 iter(self),以及额外的集合操作,如 recs1 | recs2。

对 self 进行迭代会逐条得到记录,其中每条记录本身就是一个大小为 1 的集合。您可以使用点符号访问/分配单条记录上的字段,例如 record.name。

实践:

计算总面积

在 estate.property 中添加 total_area 字段。它的定义是居住面积和花园面积之和。

计算最佳报价。

在 estate.property 中添加 best_price 字段。它被定义为报价中的最高价(即最大值)。

修改estate_perperty模型为如下代码:

class EstateProperty(models.Model):
    _name = "estate_property"
    _description = "Estate Property"

    def _get_default_date_available(self):
        # 使用 fields.Date.today() 获取当前日期,并添加三个月
        return fields.Date.today() + relativedelta(months=+3)
    
    name = fields.Char(required=True, default="Unknown")
    description = fields.Char(compute="_compute_description", store=True)
    postcode = fields.Char()
    date_available = fields.Date(copy=False, default=_get_default_date_available)
    expected_price = fields.Float(required=True)
    selling_price = fields.Float(readonly=True, copy=False)
    bedrooms = fields.Integer()
    living_area = fields.Integer()
    facades = fields.Integer()  # 外墙
    garage = fields.Boolean()
    garden = fields.Boolean()
    garden_area = fields.Integer()
    garden_orientation = fields.Selection([
        ("north", "North"),
        ("south", "South"),
        ("east", "East"),
        ("west", "West"),
    ])
    active = fields.Boolean(default=True)
    state = fields.Selection([
        ("new", "New"),
        ("offer_received", "Offer Received"),
        ("offer_accepted", "Offer Accepted"),
        ("sold", "Sold"),
        ("canceled", "Canceled"),
    ], default="new", copy=False)
    property_type_id = fields.Many2one("estate_property_type", string="Property Type")
    user_id = fields.Many2one("res.users", string="Salesperson", default=lambda self: self.env.user)
    partner_id = fields.Many2one("res.partner", string="Partner", copy=False)
    tag_ids = fields.Many2many("estate_property_tag", string="Tags")
    offer_ids = fields.One2many("estate_property_offer", "property_id", string="Offers")
    total_area = fields.Integer(compute="_compute_total_area")
    best_price = fields.Float(compute="_compute_best_price", string="Best Price")

    @api.depends("offer_ids")
    def _compute_best_price(self):
        for record in self:
            record.best_price = max(record.offer_ids.mapped("price"))

    @api.depends("living_area", "garden_area")
    def _compute_total_area(self):
        for record in self:
            record.total_area = record.living_area + record.garden_area

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

我们成功添加了Total Area和Best Price自动计算字段!

Inverse Function反函数

您可能已经注意到,计算字段默认为只读。这是意料之中的,因为用户不应该设置值。

在某些情况下,直接设置值可能还是有用的。在我们的房地产示例中,我们可以定义要约的有效期并设置有效日期。我们希望能够设置有效期或有效日期,其中一个会影响另一个。

为此,Odoo 提供了使用反函数的功能:

from odoo import api, fields, models

class TestComputed(models.Model):
    _name = "test.computed"

    total = fields.Float(compute="_compute_total", inverse="_inverse_total")
    amount = fields.Float()

    @api.depends("amount")
    def _compute_total(self):
        for record in self:
            record.total = 2.0 * record.amount

    def _inverse_total(self):
        for record in self:
            record.amount = record.total / 2.0

计算方法设置字段,而反函数则设置字段的依赖。

⚠️请注意,逆方法在保存记录时被调用,而计算方法则在每次改变其依赖关系时被调用。

实践:计算报价的有效日期。

在 estate.property.offer 模型中添加以下字段:

Field

Type

Default

validity

Integer

7

date_deadline

Date

其中 date_deadline 是一个计算字段,定义为报价中两个字段的总和:创建日期和有效期。定义一个适当的反函数,以便用户可以设置日期或有效期。

其他信息
 

计算字段默认不存储在数据库中。因此,除非定义了搜索方法,否则无法对计算字段进行搜索。这一主题超出了本培训的范围,因此我们将不作介绍。在这里可以找到一个示例。

另一种解决方案是使用 store=True 属性存储字段。虽然这通常很方便,但要注意可能会给模型增加计算负荷。让我们重新使用我们的示例:

description = fields.Char(compute="_compute_description", store=True)
partner_id = fields.Many2one("res.partner")

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

每次更改合作伙伴名称时,都会自动重新计算提及该合作伙伴的所有记录的描述!当数百万条记录需要重新计算时,重新计算的费用很快就会过高。

还值得注意的是,一个计算字段可以依赖于另一个计算字段。ORM 足够聪明,可以按照正确的顺序正确地重新计算所有依赖关系......但有时会以降低性能为代价。

一般来说,在定义计算字段时必须始终牢记性能。要计算的字段越复杂(例如,有很多依赖关系或一个计算字段依赖于其他计算字段),计算所需的时间就越长。一定要事先花些时间评估计算字段的成本。大多数情况下,只有当你的代码到达生产服务器时,你才会意识到它拖慢了整个流程。这可不好 :-(

Onchanges

在我们的房地产模块中,我们还希望帮助用户输入数据。当设置 "花园 "字段时,我们希望给出花园面积和方向的默认值。此外,当 "花园 "字段未设置时,我们希望将花园面积重置为零,并删除朝向。在这种情况下,给定字段的值会修改其他字段的值。

onchange "机制为客户端界面提供了一种方法,只要用户填写了字段值,客户端界面就可以更新表单,而无需向数据库保存任何内容。为此,我们定义了一个方法,其中 self 代表表单视图中的记录,并用 onchange() 对其进行装饰,以指定由哪个字段触发。对 self 所做的任何更改都会反映在表单上:

from odoo import api, fields, models

class TestOnchange(models.Model):
    _name = "test.onchange"

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

    @api.onchange("partner_id")
    def _onchange_partner_id(self):
        self.name = "Document for %s" % (self.partner_id.name)
        self.description = "Default description for %s" % (self.partner_id.name)

实践:设置花园面积和方向值。

在 estate.property 模型中创建一个 onchange,以便在花园设置为 True 时设置花园面积 (10) 和朝向 (North) 的值。当未设置时,清除字段。

@api.onchange("garden")
    def _onchange_garden(self):
        if self.garden:
            self.garden_area = 10
            self.garden_orientation = "north"
        else:
            self.garden_area = 0
            self.garden_orientation = ""

如何使用它们?

对于计算字段和 onchanges 的使用没有严格的规定。

在很多情况下,使用计算字段和 onchanges 可以达到相同的效果。我们总是倾向于使用计算字段,因为它们也是在表单视图的上下文之外触发的。切勿使用 onchange 向模型添加业务逻辑。这是一个非常糟糕的想法,因为在以编程方式创建记录时,不会自动触发 onchange;它们只会在表单视图中触发。

使用存储的计算字段时,请密切注意其依赖关系。当计算字段依赖于其他计算字段时,更改一个值可能会触发大量的重新计算。这会导致性能低下。

上一篇 odoo17开发教程(13):模型之间的关系-One2many一对多-CSDN博客

下一篇 odoo17开发教程(15):增加一些动作和按钮-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值