Odoo 8中计算字段筛选失效问题排查
解决Odoo发票附件数量字段搜索报错问题
问题分析
你当前的搜索方法实现存在两个核心问题:
- 效率低下:遍历所有发票并计算附件数量,数据量大时会严重拖慢系统
- 逻辑缺陷:直接在search方法中调用compute字段,容易引发上下文冲突,且报错显示返回的ID列表处理不当,导致Odoo无法解析domain条件
修复方案
方案1:优化Search方法(无需store=True)
直接通过SQL关联查询获取符合条件的发票ID,避免遍历所有记录:
attachment_count = fields.Integer(string="QAF", compute='count_attachments', search='_search_att_count') @api.one def count_attachments(self): # 保留原计算逻辑,示例: self.attachment_count = self.env['ir.attachment'].search_count([ ('res_model', '=', 'account.invoice'), ('res_id', '=', self.id) ]) def _search_att_count(self, operator, value): cr = self._cr model_name = self._name ids = [] if operator == '=': # 查询附件数量等于指定值的发票 cr.execute(""" SELECT res_id FROM ir_attachment WHERE res_model = %s GROUP BY res_id HAVING COUNT(id) = %s """, (model_name, value)) ids = [row[0] for row in cr.fetchall()] # 处理数量为0的情况:包含无任何附件的发票 if value == 0: cr.execute(""" SELECT id FROM account_invoice WHERE id NOT IN ( SELECT res_id FROM ir_attachment WHERE res_model = %s ) """, (model_name,)) ids += [row[0] for row in cr.fetchall()] elif operator == '>': # 查询附件数量大于指定值的发票 cr.execute(""" SELECT res_id FROM ir_attachment WHERE res_model = %s GROUP BY res_id HAVING COUNT(id) > %s """, (model_name, value)) ids = [row[0] for row in cr.fetchall()] elif operator == '<': # 查询附件数量小于指定值的发票(包含无附件的) cr.execute(""" SELECT id FROM account_invoice WHERE id NOT IN ( SELECT res_id FROM ir_attachment WHERE res_model = %s ) OR id IN ( SELECT res_id FROM ir_attachment WHERE res_model = %s GROUP BY res_id HAVING COUNT(id) < %s ) """, (model_name, model_name, value)) ids = [row[0] for row in cr.fetchall()] return [('id', 'in', ids)]
方案2:启用store=True并添加更新触发器(推荐)
如果需要频繁搜索,推荐将字段存储到数据库,同时添加附件变更时的更新逻辑:
1. 修改发票模型的字段定义
attachment_count = fields.Integer( string="QAF", compute='_compute_attachment_count', store=True ) @api.depends('message_attachment_ids') def _compute_attachment_count(self): for rec in self: rec.attachment_count = len(rec.message_attachment_ids)
2. 扩展ir.attachment模型,添加更新触发器
在你的模块中创建ir.attachment的继承类:
from openerp import models, api class IrAttachment(models.Model): _inherit = 'ir.attachment' @api.model def create(self, vals): res = super(IrAttachment, self).create(vals) # 当附件属于发票时,更新对应发票的附件计数 if vals.get('res_model') == 'account.invoice' and vals.get('res_id'): invoice = self.env['account.invoice'].browse(vals['res_id']) invoice._compute_attachment_count() return res def unlink(self): # 记录要删除的附件对应的发票ID invoice_ids = [att.res_id for att in self if att.res_model == 'account.invoice'] res = super(IrAttachment, self).unlink() # 更新对应发票的附件计数 if invoice_ids: self.env['account.invoice'].browse(invoice_ids)._compute_attachment_count() return res
错误原因说明
你原代码中self.search([])会获取所有发票记录集,遍历访问x.attachment_count会触发compute方法,不仅效率极低,而且在旧API的search方法上下文里,这种操作容易导致Odoo无法正确解析返回的domain条件,最终触发AssertionError。
内容的提问来源于stack exchange,提问作者odoo newbie
相关产品推荐
相关产品推荐

