1. 前言
下面进入今天主题,对odoo button和ir.actions.server的用法进行总结。
ir.actions.server是odoo五种action之一,这里和button放在一起总结,是因为它们有一些相似之处,都可以调用服务端函数。除此之外,button的功能更为灵活,将在后文介绍。
如下图先建立直观认知:
- button:form页面左上角默认有两个按钮“编辑”和“创建”,在其下方有三个我定义的按钮WITHDRAW、BACK和CANCEL;
- ir.actions.server:中部有一个动作下拉列表,可称其为上下文菜单(contextual menu),主要作用是对当前记录(form视图)或记录集(list视图)执行服务端操作,由ir.actions.server动作定义。
2. ir.actions.server介绍
上下文菜单可执行的动作有4种:
- 执行python代码
- 创建新记录
- 修改记录
- 触发执行多个服务端动作
在自定义上下文菜单中,第一种动作最常用,创建和修改记录是默认功能,除非有特殊需求,一般不需要自定义。后面三种功能,本人在odoo14自带模块中并未搜索到参考案例,可暂不研究。下面看一个案例。
Submit
code
if record and record.status in ['draft','rejected']:
record.action_submit()
else:
raise UserError('Only draft and rejected records can be submit!')
Back
code
if record:
action={
"name":"My Properties",
"type":"ir.actions.act_window",
"view_mode":"list",
"res_model":record._name,
#"res_id":record.id
}
这段xml定义了两条ir.actions.server记录,对应上图上下文菜单中两个自定义按钮“Submit”和“Back”。在定义ir.actions.server记录时,有这6个重要字段:
- name
按钮显示的文字,建议开发中统一使用英文,后期可通过odoo的国际化机制进行翻译 - model_id
按钮调用后台函数所在的模型 - binding_model_id
按钮将在该模型所在的上下文菜单中呈现 - groups_id
绑定权限组,满足权限规则才会呈现 - state
可填写code、object_create、object_write和multi,分别对应上文提到的4种动作。其中仅code在自定义中常用,表示执行python代码 - code
一段python代码,可调用model_id所绑定模型上的方法。这段代码可使用的变量包括如下这些:
env: odoo的环境变量, 可进入odoo-bin shell,通过python的自省函数dir()查看当前工作空间的所有变量,并进一步通过dir(env)查看env环境变量的属性,其中较常用的有env.uid、env.users、env.company、env.context、env.cr等,具体可到shell下查看。
model: 按钮所绑定的模型
record: 当前记录(若是list视图下,则为空)
records: 当前记录集(若是form视图下,则为空;list视图下若未选中任何记录,也为空)
time、datetime、dateutil、timezone: 一些有用的python对象
log: log(message,level='info'|...),可调用日志函数记录日志
UserError: 通过raise UserError('...')弹出告警窗口
如上面的示例代码中第二个record,通过action实现跳转。
在书写code时,注意python代码前面的空格和缩进,如上面的示例代码中所示,则为正确的书写格式。
3. button介绍
button也可以触发服务端代码、实现页面跳转,还可以触发前端js代码。且button可以通过domain条件判断设置invisible、readonly和required属性,这就使得button比上下文菜单中的按钮更具灵活性。在上下文菜单中,一般定义一些通用的按钮(如复制、删除等),而对于具有特殊条件判断的场景(比如需要根据当前记录的状态显示不同的按钮),则可以使用button。
在老版本的odoo中,button还可用于工作流workflow,新版odoo已经取消了workflow,button仅有object和action两种用法。先看如下代码。
...
这是一个定义form视图的record,在form的header标签内定义了四个button,前文图中出现了其中三个按钮WITHDRAW、BACK和CANCEL,第一个按钮SUBMIT由于条件判断激活了invisible属性。这四个按钮囊括了button的所有重要细节。
3.1 两种类型的button:object和action
首先,button分为两大类:object和action。上文示例代码中,第1、2两个按钮属于object,可调用服务端的模型函数;第3个按钮属于action,可执行页面跳转(ir.actions.act_window)、服务器动作(ir.actions.server)、链接动作(ir.actions.act_url)、客户端动作(ir.actions.client)或报表渲染动作(ir.actions.report.xml)这五种action中的任何一种;最后一个Cancel按钮属于special,并不实际执行动作。
- object类型按钮
type="object"时,表示该按钮将触发后台服务端函数,name字段指明函数名,第一个record调用action_submit函数,第二个函数调用action_withdraw函数。因此,该视图所绑定的模型上应该有这两个函数,如下所示,具体的业务逻辑可根据实际情况确定。
def action_submit(self):
print(self)
print(self.env.context)
for x in self:
x.status='under review'
return True
def action_withdraw(self):
for x in self:
x.status='draft'
return True
- action类型按钮
type="action"时,表示该按钮将触发动作,name字段使用"%(action_xml_id)d"的方式指明将要执行的动作,%(...)d的写法是预处理命令,odoo引擎在将该条record加载到数据库时,会计算出该xml id所对应的ir.model.data表中的内部id。
3.2 button的一些属性
除了上一小节已经介绍的type和name属性,button还有这几个重要属性:string、icon、context、attrs和class。
- string
按钮上显示的文字。 - icon
可以在按钮上显示Font Awesome图标,使用方法是:首先到Font Awesome官网搜索心怡的图标,找到对应的名字,比如step-backward这个图标用于BACK按钮就挺合适;在icon属性中给这个图标名加fa前缀,如icon="fa-step-backward",则可出现如下图效果。
- context
可向前端或后端传递上下文变量,使用字典的方式。后台函数可用self.env.context.get('key')的方式获取传递过来的变量。 - attrs
attrs属性是一个字典,该字典的键名可以是invisible、readonly或者required中的一种(button的attrs仅支持invisible这一种),键值是一个domain条件判断,如下所示。关于domain条件判断的写法,下一节做一个总结。
attrs="{'invisible':[('status','in',['under review','online','deal'])]}"
4. domain写法
domain的基本单元是一个三元组,由3个元素组成 (字段名,操作符, 值),多个元组可进行逻辑计算,采用波兰表达式(前缀表达式),运算符在最前面,待逻辑计算的元组操作数在后面。如下面这个逻辑演算:
[('name','=','Helen'), '|', ('sex', '=', 'male'), ('age', '>', '40')]
元组间的逻辑前缀有如下三种:
逻辑前缀 | 说明 |
---|---|
& | 'AND','&',表示后面两个元组同时满足。这是默认关系,可以省略。 |
| | 'OR','|', 表示后面两个元组至少满足一个。 |
! | 'NOT','!', 表示将后面一个元组的条件反转。 |
元组内的操作符有如下几种:
操作符 | 说明 |
---|---|
=,>.<,>=,<=,!= | 比较运算,等于,不等于,大于,大于等于,小于,小于等于 |
like | 模糊匹配,通过%value%匹配,类似sql中的like |
=like | 可以使用正则匹配 |
ilike | 类似like,但是忽略大小写 |
=ilike | 类似=like,但是忽略大小写 |
not like | 与%value%不匹配的 |
not ilike | 类似not like,但是忽略大小写 |
=? | 未设置或者等于,未设置表示当值是None或者是False,其余和=一样 |
in | 判断value是否在列表里面 |
not in | 判断value是否不在列表里面 |
child_of | 判断是否是value的子记录 |