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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:17:33