Odoo10中带store=True的计算字段tot_comp_survey为何大库升级后数据库值为null
问题根因与排查修复方案
核心问题定位
你遇到的两个现象(大库升级后持久化字段全为NULL、大库升级流程极慢)是两个实现疏漏叠加导致的:
- 作为带
store=True的计算字段,没有给计算方法配套必填的@api.depends依赖声明,ORM无法正确识别字段的持久化触发逻辑,批量初始化计算的流程容易出现异常中断。 - 原计算方法的实现存在严重的性能缺陷,数据量稍大就会触发超时中断,事务回滚后所有字段值停留在表结构刚创建时的初始NULL状态:
- 实现中先通过
search把所有关联的survey.user_input记录全量拉取到Odoo服务进程内存中,没有做任何聚合裁剪 - 后续循环遍历每条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
相关产品推荐
相关产品推荐

