即使一个人,也要活得像军队一样!

Odoo13-- O2M字段中下拉列表去重实现(非公用方法)

在实际开发中,我们又是会遇到一些需求,以销售订单为例:
在销售订单明细中, 我们添加一行明细:产品为苹果。再添加一行明细时候,产品的下来选项就不能含有苹果的产品。
如下图:

去重

关键代码:

tools.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
from functools import reduce

def get_record_vals(self, model, domain):
"""
@author: LoneyCao 2019/12/14 9:30
@desc: 获取记录的字典。注意:只针对单条记录。
@params self: 实列
@params model: 模型名
@params domain: domain表达式
@return: 返回一个dict,包含记录的各字段值。
"""
vals_list = self.env[model].search_read(domain)
if vals_list:
vals = reduce(lambda x, y: dict(**x, **y), vals_list)
return vals
return False


def get_o2m_field_page_cach_val(self, o2m_model, o2m_val, field_list=None, field_map=None):
"""
@author: LoneyCao 2019/12/14 9:30
@desc: 编辑状态下,获取Form视图上O2M字段的当前记录
@params self: 实列
@params o2m_model: O2M字段关联的模型名
@params o2m_val: 当前编辑状态下,O2M字段的所有记录。如:[(0,0,{}),(1,30,{}),(2,31,False),(4,0,False)]
@params field_list: 字段列表,如果指定,则返回一个字段所有值的字典。如下所示:
field_list=[name, age]
return {'name': ['Alex', 'Tom', 'Jerry'], 'age': [20,30,30]}
注意:与参数field_map不可共存。
@params field_map: 字段映射,如果指定,则返回一个字段与字段的映射关系。如下所示:
field_map=[age, name]
return {'20': ['Alex'], '30': ['Tom', 'Jerry']}
注意:与参数field_list不可共存。
@return: 返回一个list,包含当前的记录
"""
records = [] # 当前编辑状态下的记录
for rec in o2m_val:
vals = {}
if rec[0] == 0: # 新增
vals = rec[2]
elif rec[0] == 1: # 修改
vals = get_record_vals(self, o2m_model, [('id', '=', rec[1])]) # 先获取原来记录
vals.update(rec[2]) if vals else False # 再更新
elif rec[0] == 4: # 不变
vals = get_record_vals(self, o2m_model, [('id', '=', rec[1])]) # 获取原来记录
if vals:
records.append(vals)

field_dict = {}

if field_list:
for field in field_list:
v_list = []
for rec in records:
v = rec.get(field)
if isinstance(v, tuple): # m2o类型字段
v = v[0]
if v:
v_list.append(v)
field_dict[field] = v_list

return field_dict

if field_map:
for rec in records:
k = rec.get(field_map[0])
v = rec.get(field_map[1])
if isinstance(k, tuple): # m2o类型字段
k = k[0]
if isinstance(v, tuple):
v = v[0]
if k and v:
if k in field_dict:
field_dict[k].append(v)
else:
field_dict[k] = [v]

return field_dict

return records
1
2
3
4
5
6
7
8
9
10
11
class ProductProduct(models.Model):
_inherit = 'product.product'

@api.model
def _name_search(self, name='', args=None, operator='ilike', limit=100):
order_line = self._context.get('order_line')
if order_line:
ids = get_o2m_field_page_cach_val(self, 'sale.order.line', order_line, ['product_id']).get('product_id', [])
args = expression.AND([args, [('id', 'not in', ids)]]) # 去重

return super(ProductProduct, self)._name_search(name=name, args=args, operator=operator, limit=limit)

xml文件

1
<field name="product_id" context="{'order_line':parent.order_line }"  />

这部分代码,根据实际需求编写。

说一说原理

对于关系型字段,在下拉框选择时候,会调用对应模型的name_search方法,通过此方法,返回数据。

所以,我们只要在这里进行过滤即可。

版本一

在表头定义一个隐藏的计算字段,用于记录子表的所有产品的值,组成一个domain。
在下拉框选择时候,在name_search方法中处理。

版本二

直接把条件传入'name_search方法。在方法中处理。

其他的动态domain过滤:
onchange使用动态过滤时,在保存后,再编辑,就会失效。

-------------本文结束感谢您的阅读-------------