You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 02:52:50