一般我们在开发时,many2one字段都是默认出现全部的记录,像下图所示
近期有一个新的需求,要求根据部门不同的选择,访问人只能选择该部门下的员工,不能选择其他部门的员工,如下图所示
那么应该如何实现
首先先介绍一下当前的模型
上图中的form表单是模型A,访问人是'hr.employee'模型的many2one字段,访问部门是'hr.department'模型
这是模型A中的字段
department_id = fields.Many2one('hr.department', string='访问部门')
user_id = fields.Many2one('hr.employee', string='访问人')
这是'hr.employee'模型中的部门字段,它是在员工模块中连接部门模型的多对一字段
首先对于hr原生模块的源代码不做修改,我们直接继承'hr.employee'模型并写在类B中写name_search重新方法
class B类类名(models.Model):
_inherit = 'hr.employee'
@api.model
def name_search(self, name='', args=None, operator='=', limit=100):
department_id = self.env.context.get("department_id")
# 上面这里是接收我们从模型A中传过来的部门下拉框中选择的值
# 下面是对于传过来的值进行判断,如果传过来的是空值,即还没进行选择的时候,不做以下的操作
if department_id:
mods = self.env['hr.employee'].search([('department_id', '=', department_id)])
# 对有值的部门值去对员工模型做查找,让模型中部门字段和传过来的部门字段去匹配,
# 匹配成功的记录就是我们拿到的模型对象 我这里拿到的是hr.employee(4,3),
# 对这两个模型的id在下面进行提取
ids = [mod.id for mod in mods]
# 提取出来的id列表就是即将去查找的域,让搜索的范围以id是否在我们的域中,符合即选取
args = [('id', 'in', ids)]
# 通过下方的固定写法去真正执行name_search方法
return super(authority_management_employee, self).name_search(name, args, operator, limit)
那么在对应的员工模块中写好了方法,现在需要从模型A中传值过来
只需要进入到模型A写好的视图中,找到邀请人的field
<record id="模型A视图id名_form" model="ir.ui.view">
<field name="name">模型A的form</field>
<field name="model">模型A模型名</field>
<field name="arch" type="xml">
<form>
.....
<field name="user_id"/>
加上context传输上下文,将部门的值传过去
<field name="user_id" context = "{'department_id':department_id}"/>
注意:这里的department_id的字段在使用传值上下文时,同一表单内一定要有这个字段的显示,就算这个字段并不想展现在表单也要在表单中隐藏像如下所示:
<field name="department_id" invisible="1" />
如果没有这个字段在form标签中就会报js的错误,一定要注意。
然后重启服务,就能实现效果
可以看到数据是正确的,并没有匹配歪
这一效果仅针对有传输上下文参数的字段值,同一模型中的其他的同模型的多对一字段没有传输上下文则不受影响。
而对于项目全局,其他模块也有可能会有用到这一字段去选择员工,但又不想所有的表单中的同一多对一字段传输上下文参数都有同一效果(例如模型A中是针对部门筛选,模型C中是针对年龄筛选),那么为了避免冲突,可以增加一个上下文传值'active_model':active_model
<field name="user_id" context = "{'department_id':department_id,'active_model':active_model}"/>
这样就将当前所点击的多对一下拉选框时,该字段所处于的模型名传输进上下文,然后在员工模型中的name_search方法中增加条件控制
model = self.env.context.get("active_model")
获取到活动模型
if model == "模型A的模型名":
对传来的值与我们要控制生效的模型名匹配
到了代码中位置如下
@api.model
def name_search(self, name='', args=None, operator='=', limit=100):
model = self.env.context.get("active_model")
if model == "模型A的模型名":
#增加上面两行对效果范围进行控制
department_id = self.env.context.get("department_id")
if department_id:
......
这样就只对我们想生效的模型生效效果了。
需要注意,仅书写了上方的方法,当部门字段再次手动修改选择了其他部门的时候,访问人字段的值是不会发生重置或修改的,需要再次手动选择对应部门下的员工,如果想要实现修改部门就清空访问人字段的值,可以在模型A中添加方法和onchange的api去监控部门字段是否发生改变,激活后执行方法将访问人字段清空即可。
class A类类名(models.Model):
_name = '模型A模型名'
@api.onchange('department_id')
def onchange_department_clean_user(self):
self.user_id = False
按上面代码书写即可实现效果。
本文章于2023年1月由红星编程实验室免费对公众开放。