第十章 button触发 action
到目前为止我们通过声明字段和视图的方式几乎完成了我们的模块。 我们仅仅通过前面章节的计算字段和onchanges添加了商业逻辑。 在任何真实的商业情境下,我们希望通过按钮链接到商业逻辑,在我们的房地产例子中,我们希望能够:
- 取消 或者 设置房产已经售出。
- 接受或者拒绝一个报价
可能有人争辩说,我们可以通过手工改变状态做到这些。 但是这并不方便,还有,我们希望添加一些额外的处理,当一个报价被接受的时候,我们希望设置房产的销售价格和购买人。
Action Type
参考:关于这个主题的文档在这里 Actions and Error management.
Note
Goal: 在这个小节结尾
- 你可以将一处房产取消或者标识为已经售出。
一处已经标记为取消的房产不能再被标记为售出,已经售出的房产也不能再被取消。为了清晰起见,我们在视图中添加了state字段。
你可以接受或者拒绝一个报价。
- 一旦一个报价被接受,selling price和buyer应该被设置。
在我们的房地产模块中,我们想通过一下按钮来实现商业逻辑,通常的做法是:
- 在视图中添加按钮,例如在一个视图的header中
<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”,name属性赋值一个方法名, Odoo框架会执行对应模型中python方法。
第一个重要细节是python中的方法名不能以“_”开头,我们需要一个公共方法,可以直接被odoo接口调用(通过RPC)。 直到现在,所有生成的方法(compute,oncange) 都是内部调用,所以我们下划线作为私有方法的前缀,你应该一直定义私有方法,除非你需要他们被用户接口调用。
同时注意: 在self中用循环语句,总是假设这个方法可以被多条记录调用,这有利于方法的重用。
最后,一个公共方法总应该有返回值,这样它可以被XML-RPC调用,当不确定的时候,只要返回True。
在Odoo源代码中有数百个例子,One example is this button in a view and its corresponding Python method
练习: 取消和设置房产为已售
-
在estate.property 模型中增加按钮“取消”和“已售”,一个已经取消的房产不能设置为已售,一个已售出的房产不能标记为取消。
实现第一张图片的效果
Tip: 为了引发错误,你可以使用 UserError function. 在odoo源代码中有很多例子;-)
<header> <button name="do_sold" type="object" string="售出" class="btn btn-info" attrs="{'invisible':['|',('state','=','3'),('state','=','4')]}"> 售出 </button> <button name="do_cancle" type="object" string="取消" class="btn btn-info" attrs="{'invisible':[('state','=','4')]}"> 取消 </button> </header>
def do_sold(self): for rec in self: if rec.state=="4": # 已经取消,不能再次售出 raise odoo.exceptions.UserError(message='已经取消,不能被售出') else: rec.state="3" def do_cancle(self): for rec in self: if rec.state=="3": # 已经取消,不能再次售出 raise odoo.exceptions.UserError(message="已经售出,不能取消") else: rec.state="4"
-
在estate.property.offer模型中增加“接受”和“拒绝”按钮
实现第二张图片的效果
Tip: 用图标作为按钮,看看这个例子 at this example.
<tree string="Channel" editable="bottom"> <field name="partner_id"/> <field name="price"/> <field name="validity"/> <field name="date_deadline"/> <button name="action_confirm" string="Confirm" type="object" icon="fa-check"/> <button name="action_cancel" string="Cancel" type="object" icon="fa-times"/> <field name="status"/> </tree>
-
当一个报价被接受,给相应的房产设置购买人和销售价格
实现第三张图片的效果
注意: 在实际生活中,对于给定的房产只有一个报价可以被接受。
def action_confirm(self): for rec in self: count = self.env['estate.property.offer'].search_count(domain=[("property_id","=",rec.property_id.id),("status","=","1")]) if count==0: # 没有报价被接受 rec.status="1" rec.property_id.buyer = rec.partner_id rec.property_id.selling_price = rec.price else: raise odoo.exceptions.UserError("已经有报价被接受!") def action_cancel(self): self.status="2"
注意: rec.property_id 这他妈不是一个id,而是一个对象
Object Type
在第六章中,我们创建了一个动作连接到一个菜单,你可能会想有没有可能将这个动作链接到按钮,好消息,有一种方法可以这样做:
<button type="action" name="%(test.test_model_action)d" string="My Action"/>
我们使用type="action"并且name属性指向动作的外部id
在下一章,我们将看到如何保证数据的正确性。