12. 操作处理
到目前为止,主要通过声明字段和视图来构建模块。在上一章中,我们借助计算字段和 onchange 事件引入了业务逻辑。在任何真实的业务场景中,通常希望将某些业务逻辑与操作按钮关联起来。在estate示例中,希望能够:
取消或将房产标记为已售出;
接受报价或拒绝报价;
有人可能会说,通过手动更改状态来实现这些功能,但这并不方便。要添加一些额外处理:当报价被接受时,需要设置房产的售价和买家。
对象类型
开发实践:实现将房产标记为取消或已出售的操作:

一旦报价被接受,就应确定售价和买方:

在estate模块中,将业务逻辑与某些按钮关联起来。最常见的方法是:在视图中添加一个按钮,例如,在视图的页眉处:
<form>
<header>
<button name="action_do_something" type="object" string="Do Something"/>
</header>
<sheet>
<field name="name"/>
</sheet>
</form>并将此按钮与业务逻辑关联:
from odoo import fields, models
class TestAction(models.Model):
_name = "test.action"
name = fields.Char()
def action_do_something(self):
for record in self:
record.name = "Something"
return True通过为按钮设置 type="object",Odoo 框架将在指定的模型上执行名称为 name="action_do_something" 的 Python 方法。
首先需要注意的一点是,方法名称前缀没有添加下划线 (_)。这使得该方法成为公共方法,可以直接从 Odoo 界面(通过 RPC 调用)调用。到目前为止,创建的所有方法(compute、onchange)都是在内部调用的,因此使用了以下划线(_)开头的私有方法。除非需要从用户界面调用,否则应始终将方法定义为私有。
注意: 使用
self进行循环时,应始终假设一个方法可以对多个记录进行调用;这样更有利于方法的复用。
最后,公共方法应始终返回某个值,以便可以通过 XML-RPC 调用。如果不确定,直接返回 True即可。
Odoo 源代码中有数百个示例。其中一个示例是视图中的这个按钮及其对应的 Python 方法。
操作类型
在第 5 章节《界面》中,我们创建了一个与菜单关联的操作。可能在想,是否可以将操作与按钮关联起来。当然可以!实现方法之一是:
<button type="action" name="%(test.test_model_action)d" string="My Action"/>这里使用 type="action",并在名称中引用外部标识符。在下一章中,我们将探讨如何在 Odoo 中防止录入错误数据。
开发实践
实现将房产设置为取消或已售出的操作
在
estate.property模型中添加“取消”和“已售出”按钮操作。"已取消"的房产无法标记为"已售出",已被标记为“已售出”的房产也无法取消。在
estate.property.offer模型中添加“接受”和“拒绝”按钮。当报价被接受时,房产应关联设定买家和售价,且同一处房产只能接受一份报价。
打开文件:models/property.py ,新增以下两个方法:
# 在 `estate.property` 模型中添加“取消”和“已售出”按钮操作。"已取消"的房产无法标记为"已售出",已被标记为“已售出”的房产也无法取消。
def action_sold_btn(self):
""" [已售出]按钮: 对应的操作 """
for record in self:
if record.state == 'canceled':
raise exceptions.UserError("【已取消】状态的记录不能标记为【已售出】")
if record.state != 'accepted':
raise exceptions.UserError("只能在报价已接受的状态下标记为【已售出】")
record.state = 'sold'
print(f"action_sold_btn.record=> {record.id}")
def action_cancel_btn(self):
""" [取消]按钮: 对应的操作 """
for record in self:
if record.state == 'sold':
raise exceptions.UserError("【已售出】状态的记录不能取消")
record.state = 'canceled'
print(f"action_cancel_btn.record=> {record.id}")打开文件:models/property_offer.py ,新增以下两个方法:
# 在 `estate.property.offer` 模型中添加“接受”和“拒绝”按钮。
def action_accepted(self):
""" 接受报价 """
for offer in self:
offer.write({'status': 'accepted'})
offer.property_id.selling_price = offer.price
offer.property_id.buyer_id = offer.partner_id
offer.property_id.write({'state': 'accepted'})
def action_refused(self):
""" 拒绝报价 """
for offer in self:
offer.write({'status': 'refused'})