如何在Odoo中重写ir_attachment类的_file_write方法?
嘿,我来帮你搞定Odoo里重写ir_attachment的_file_write方法这件事,结合你提到的Odoo 9的源码注释,给你一步步拆解正确的实现方式,还有可能踩的坑:
重写
ir_attachment的_file_write方法(Odoo 9) 基础重写步骤
首先得在自定义模块里继承核心的ir.attachment模型,这是Odoo扩展核心功能的标准姿势。比如在模块的models目录下新建ir_attachment.py,代码如下:
from openerp import models, api class IrAttachment(models.Model): _inherit = 'ir.attachment' # 继承核心模型 @api.model # 注意:_file_write是模型级方法,必须用这个装饰器 def _file_write(self, value, checksum): # 如果你需要保留Odoo默认的存储逻辑,先调用父类方法 res = super(IrAttachment, self)._file_write(value, checksum) # 这里加你的自定义逻辑,比如记录存储日志、修改存储路径规则等 # 举个例子:记录附件存储的校验码 # self.env['ir.logging'].create({ # 'name': 'Attachment Storage Log', # 'message': f'Attachment saved with checksum: {checksum}, store path: {res}' # }) return res # 必须返回字符串,作为store_fname的值
写完后别忘了在模块的__openerp__.py里把这个模型文件加进去,安装模块后就能生效了。
针对伪URI的存储引擎扩展(对应源码注释)
源码注释里提到的伪URI(比如hdfs://hadoppserver),核心思路是通过store_fname字段存储这个自定义URI,然后在读写删方法里判断URI前缀,走不同的存储逻辑。
比如我们要扩展HDFS存储,重写_file_write的代码可以改成这样:
from openerp import models, api import io class IrAttachment(models.Model): _inherit = 'ir.attachment' @api.model def _file_write(self, value, checksum): # 先读取系统配置里的存储URI(你可以自己加配置项) storage_uri = self.env['ir.config_parameter'].get_param('attachment.storage.uri', '') # 判断是否使用自定义存储引擎 if storage_uri.startswith('hdfs://'): # 这里写你的HDFS写入逻辑,比如调用HDFS客户端 # 示例(需要你自己实现HDFS客户端的调用): # hdfs_client = self._get_hdfs_client(storage_uri) # file_path = f'/odoo_attachments/{checksum}' # hdfs_client.write(file_path, io.BytesIO(value)) # 返回自定义的伪URI,作为store_fname存在数据库里 return f"{storage_uri}/odoo_attachments/{checksum}" else: # 走Odoo默认的本地存储逻辑 return super(IrAttachment, self)._file_write(value, checksum) # 配合重写_file_read,实现自定义存储的读取 @api.model def _file_read(self, fname, bin_size=False): if fname.startswith('hdfs://'): # 这里写HDFS读取逻辑 # hdfs_client = self._get_hdfs_client(fname.split('//')[0] + '//' + fname.split('//')[1].split('/')[0]) # file_content = hdfs_client.read(fname) # return file_content if not bin_size else len(file_content) pass else: return super(IrAttachment, self)._file_read(fname, bin_size) # 同理,重写_file_delete处理自定义存储的文件删除 @api.model def _file_delete(self, fname): if fname.startswith('hdfs://'): # 这里写HDFS删除逻辑 # hdfs_client = self._get_hdfs_client(fname.split('//')[0] + '//' + fname.split('//')[1].split('/')[0]) # hdfs_client.delete(fname) pass else: return super(IrAttachment, self)._file_delete(fname)
你可能遇到的重写问题排查
你说按常规方式重写出问题,大概率是这几个坑:
- 装饰器用错:
_file_write是模型级方法,必须用@api.model,不能用@api.multi或者@api.one(Odoo 9里这几个装饰器区分很严格) - 返回值错误:这个方法必须返回字符串(作为
store_fname的值),如果返回空或者格式不对,直接导致附件无法读取 - 权限问题:自定义模块必须依赖
base模块,并且要确保模块安装后,当前用户有权限操作ir.attachment模型 - super调用顺序:如果你的逻辑需要先执行自定义操作再走默认逻辑,要调整super的位置,但通常先调用父类方法再加自己的逻辑更稳妥
验证重写是否生效
安装模块后,上传一个附件,然后去数据库里查ir_attachment表的store_fname字段,看看是不是符合你预期的格式;再测试下载附件,确认能正常打开,这样就说明重写成功了。
内容的提问来源于stack exchange,提问作者Pengo
相关产品推荐
相关产品推荐

