Odoo中如何将非存储计算字段用作分组字段?
问题
定义了如下非存储计算字段:
SCHEDULE_SELECTION = [ ("no_schedule", "Без терміну"), ("overdue", "Протерміновано"), ("today", "Сьогодні"), ("this_week", "Цього тижня"), ("next_week", "Наступного тижня"), ("after_2_week", "Більше 2 тижнів"), ] schedule_selection = fields.Selection(selection=SCHEDULE_SELECTION, string="Групування по терміну", compute='_compute_schedule_date') def _compute_schedule_date(self): for i in self: ... if not i.schedule_date or i.done: i.schedule_selection = "no_schedule" elif i.is_overdue: i.schedule_selection = "overdue" else: selection_difference = (i.schedule_date - now).days if i.schedule_date.date() == now_date: i.schedule_selection = "today" elif selection_difference <= 7: i.schedule_selection = "this_week" elif selection_difference <= 14: i.schedule_selection = "next_week" else: i.schedule_selection = "after_2_week"
尝试使用该字段做分组过滤器时报错:
<!-- this don`t work, i receive an error --> <filter string='Термін' name='schedule_selection' domain="[]" context="{'group_by': 'schedule_selection'}"/>
由于该字段依赖其他存储/计算字段及当前时间,无法设置store=True。查到需重写search方法,但未找到具体示例,请问如何解决?
解决方案
Odoo中非存储计算字段无法直接支持搜索和分组,需要通过重写search和_read_group方法,将计算字段的逻辑转为SQL层面的条件实现。
1. 重写search方法处理搜索逻辑
将schedule_selection的筛选条件转换为对应存储字段的域条件,替换原有的计算字段搜索逻辑:
from odoo import models, fields, api from datetime import datetime, timedelta class YourModel(models.Model): _inherit = 'your.model.name' # 替换为实际模型名 # 原有字段定义... @api.model def search(self, args, offset=0, limit=None, order=None, count=False): new_args = [] now = datetime.now() now_date = now.date() week_later = now + timedelta(days=7) two_weeks_later = now + timedelta(days=14) for domain in args: if domain[0] == 'schedule_selection': op = domain[1] value = domain[2] # 匹配每个选项对应的存储字段域条件 if value == 'no_schedule': domain_expr = ['|', ('schedule_date', '=', False), ('done', '=', True)] elif value == 'overdue': # 若is_overdue也是非存储字段,需替换为schedule_date < now的逻辑 domain_expr = [('is_overdue', '=', True)] elif value == 'today': domain_expr = [ ('schedule_date', '>=', fields.Datetime.to_datetime(now_date)), ('schedule_date', '<=', fields.Datetime.to_datetime(now_date + timedelta(days=1))) ] elif value == 'this_week': domain_expr = [ ('schedule_date', '>', now), ('schedule_date', '<=', week_later) ] elif value == 'next_week': domain_expr = [ ('schedule_date', '>', week_later), ('schedule_date', '<=', two_weeks_later) ] elif value == 'after_2_week': domain_expr = [('schedule_date', '>', two_weeks_later)] else: domain_expr = domain # 适配不同搜索操作符,这里以=和in为例 if op == '=': new_args.extend(domain_expr) elif op == 'in': or_domains = [] for val in value: if val == 'no_schedule': or_domains.append(['|', ('schedule_date', '=', False), ('done', '=', True)]) elif val == 'overdue': or_domains.append([('is_overdue', '=', True)]) elif val == 'today': or_domains.append([ ('schedule_date', '>=', fields.Datetime.to_datetime(now_date)), ('schedule_date', '<=', fields.Datetime.to_datetime(now_date + timedelta(days=1))) ]) # 其余选项的in逻辑同理补充 # 合并多选项的OR条件 if or_domains: combined = or_domains[0] for dom in or_domains[1:]: combined = ['|'] + combined + dom new_args.append(combined) else: new_args.append(domain) return super(YourModel, self).search(new_args, offset=offset, limit=limit, order=order, count=count)
2. 重写_read_group方法支持分组
手动计算每个schedule_selection分组的记录数和域条件,实现分组展示:
@api.model def _read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True): if groupby and 'schedule_selection' in groupby: now = datetime.now() now_date = now.date() week_later = now + timedelta(days=7) two_weeks_later = now + timedelta(days=14) groups = [] # 遍历所有选项生成分组数据 for key, label in self.SCHEDULE_SELECTION: # 生成当前分组的域条件 if key == 'no_schedule': group_domain = domain + ['|', ('schedule_date', '=', False), ('done', '=', True)] elif key == 'overdue': group_domain = domain + [('is_overdue', '=', True)] elif key == 'today': group_domain = domain + [ ('schedule_date', '>=', fields.Datetime.to_datetime(now_date)), ('schedule_date', '<=', fields.Datetime.to_datetime(now_date + timedelta(days=1))) ] elif key == 'this_week': group_domain = domain + [ ('schedule_date', '>', now), ('schedule_date', '<=', week_later) ] elif key == 'next_week': group_domain = domain + [ ('schedule_date', '>', week_later), ('schedule_date', '<=', two_weeks_later) ] elif key == 'after_2_week': group_domain = domain + [('schedule_date', '>', two_weeks_later)] else: continue # 计算分组记录数 count = self.search_count(group_domain) if count > 0 or not lazy: groups.append({ 'schedule_selection': key, '__domain': group_domain, '__count': count, # 如需聚合其他字段,在此补充计算逻辑 }) # 若存在多级分组,需递归处理后续分组字段(此处简化处理) if len(groupby) > 1: pass return groups return super(YourModel, self)._read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
注意事项
- 若
is_overdue也是非存储计算字段,需将其逻辑替换为schedule_date < datetime.now()这类可直接在SQL中执行的条件。 - 确保
now的处理逻辑与_compute_schedule_date完全一致,避免分组结果和字段显示值不一致。 - 如需支持
!=等更多搜索操作符,需在search方法中补充对应逻辑。
内容的提问来源于stack exchange,提问作者Wald Sin
相关产品推荐
相关产品推荐

