You need to enable JavaScript to run this app. Odoo10中带store=True的计算字段tot_comp_survey为何大库升级后数据库值为null
优惠活动
大模型
产品
解决方案
定价
更多

Odoo10中带store=True的计算字段tot_comp_survey为何大库升级后数据库值为null

问题根因与排查修复方案

核心问题定位

你遇到的两个现象(大库升级后持久化字段全为NULL、大库升级流程极慢)是两个实现疏漏叠加导致的:

  • 作为带store=True的计算字段,没有给计算方法配套必填的@api.depends依赖声明,ORM无法正确识别字段的持久化触发逻辑,批量初始化计算的流程容易出现异常中断。
  • 原计算方法的实现存在严重的性能缺陷,数据量稍大就会触发超时中断,事务回滚后所有字段值停留在表结构刚创建时的初始NULL状态:
    1. 实现中先通过search把所有关联的survey.user_input记录全量拉取到Odoo服务进程内存中,没有做任何聚合裁剪
    2. 后续循环遍历每条survey,再对内存中的全量记录做匹配过滤,时间复杂度随survey数量、user_input总量呈线性增长
      这个逻辑在8条survey的小库中,因为关联的user_input总量极少,整个计算流程几秒就能完成,事务正常提交,所以字段值显示正常;在1000条survey的大库中,关联的user_input可能达到数万甚至数十万条,全量拉取+内存过滤的过程会消耗大量内存和执行时间,一旦超过Odoo worker的超时阈值,进程被强制终止,整个计算的数据库事务直接回滚,最终表现为数据库中的tot_comp_survey全为NULL。

排查步骤

  • 首先查看模块升级阶段的Odoo服务日志,重点定位是否存在worker超时、内存溢出、数据库连接断开类的报错,这是大库下计算流程中断的直接证据。
  • 在调试模式下选取10条现有survey记录,手动调用_compute_survey_statistic方法,检查字段是否能正常赋值:如果手动调用能正常生成值,说明问题完全来自全量升级时的性能不足导致的流程中断。
  • 检查计算方法是否正确挂载了@api.depends装饰器:没有依赖声明的store=True计算字段,ORM不会将其纳入正常的持久化维护流程。

修正实现方案

1. 补上必填的依赖声明

带store=True的计算字段必须通过@api.depends声明关联的触发字段,否则ORM不会在关联数据变化时自动更新字段值,也无法正常完成批量初始化计算。

2. 优化计算逻辑,替换低性能的全量拉取写法

所有统计类计算优先使用ORM提供的read_group接口,直接在数据库层面完成分组聚合,不需要把全量记录拉取到应用层做内存过滤,性能可以提升数个数量级,从根源解决大库下的超时问题。

修正后的完整代码参考:

from odoo import api, fields, models

class Survey(models.Model):
    _inherit = 'survey.survey'

    tot_comp_survey = fields.Integer(
        "Number of completed surveys",
        compute="_compute_survey_statistic",
        store=True
    )

    @api.depends('user_input_ids.state', 'user_input_ids.type')
    def _compute_survey_statistic(self):
        UserInput = self.env['survey.user_input']
        # 空记录集直接返回,避免无意义查询
        if not self.ids:
            return

        # 数据库层面聚合统计,避免全量拉取记录
        sent_stats = UserInput.read_group(
            domain=[('survey_id', 'in', self.ids), ('type', '=', 'link')],
            fields=['survey_id'],
            groupby=['survey_id']
        )
        start_stats = UserInput.read_group(
            domain=[
                ('survey_id', 'in', self.ids),
                ('state', 'in', ['skip', 'done'])
            ],
            fields=['survey_id'],
            groupby=['survey_id']
        )
        complete_stats = UserInput.read_group(
            domain=[('survey_id', 'in', self.ids), ('state', '=', 'done')],
            fields=['survey_id'],
            groupby=['survey_id']
        )

        # 生成映射字典,O(1)时间复杂度完成匹配
        sent_map = {stat['survey_id'][0]: stat['survey_id_count'] for stat in sent_stats}
        start_map = {stat['survey_id'][0]: stat['survey_id_count'] for stat in start_stats}
        comp_map = {stat['survey_id'][0]: stat['survey_id_count'] for stat in complete_stats}

        for survey in self:
            survey.tot_sent_survey = sent_map.get(survey.id, 0)
            survey.tot_start_survey = start_map.get(survey.id, 0)
            survey.tot_comp_survey = comp_map.get(survey.id, 0)

优化效果

  • 计算流程从原来的"全量拉取-内存过滤"变成"数据库聚合-内存赋值",执行时间从分钟级降到秒级,完全不会触发大库下的超时中断,事务正常提交后字段值会正确持久化到数据库中。
  • 正确声明依赖后,后续survey的作答状态、类型变化时,ORM会自动触发对应字段的重算并持久化,不需要手动干预。

内容的提问来源于stack exchange,提问作者Akhmad Ali

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.31 07:45:10