如何在用户下载ir_attachment后删除附件以保持数据库清洁?
解决方案
方法1:用定时任务清理临时附件
适合无需立即删除的场景,通过标记临时附件,后台定期自动清理。
步骤1:扩展ir.attachment添加临时标记字段
from odoo import models, fields class IrAttachment(models.Model): _inherit = 'ir.attachment' is_temp = fields.Boolean(string='临时附件', default=False)
步骤2:创建附件时标记为临时
修改你的创建代码,添加临时标记:
attachment = self.env["ir.attachment"].create({ 'name': file_name, 'type': 'binary', 'datas': base64.b64encode(zip_data).decode(), 'res_model': self._name, 'store_fname': file_name, 'mimetype': 'application/zip', 'is_temp': True, # 标记为临时附件 }) # 后续下载代码保持不变
步骤3:创建定时任务自动清理
进入Odoo后台【设置】→【技术】→【自动化】→【定时动作】,新建定时任务:
- 模型:
ir.attachment - 执行动作:执行Python代码
- 代码内容:
from datetime import timedelta self.search([ ('is_temp', '=', True), ('create_date', '<', (fields.Datetime.now() - timedelta(hours=1))) ]).unlink()
- 执行频率:根据需求设置(如每小时一次)
方法2:前端下载完成后立即删除附件
实现下载完成后即时清理,需配合前端JS和自定义控制器。
步骤1:创建删除附件的控制器
from odoo import http from odoo.http import request class TempAttachmentController(http.Controller): @http.route('/web/temp_attachment/delete', type='json', auth='user') def delete_temp_attachment(self, attachment_id): attachment = request.env['ir.attachment'].browse(int(attachment_id)) if attachment.exists(): attachment.unlink() return {'success': True}
步骤2:修改返回动作,触发前端逻辑
替换原act_url返回,改用客户端动作:
return { 'type': 'ir.actions.client', 'tag': 'download_and_delete_attachment', 'params': { 'attachment_id': attachment.id, 'download_url': f'/web/content/{attachment.id}?download=true', } }
步骤3:添加前端JS处理下载与删除
在模块static/src/js目录下创建download_handler.js:
odoo.define('your_module.download_handler', function(require) { 'use strict'; var core = require('web.core'); var ActionManager = require('web.ActionManager'); ActionManager.include({ execute_action: function(action, options) { if (action.tag === 'download_and_delete_attachment') { var params = action.params; // 创建隐藏a标签触发下载 var a = document.createElement('a'); a.href = params.download_url; a.download = ''; document.body.appendChild(a); a.click(); document.body.removeChild(a); // 轮询检查下载状态,完成后调用删除接口 var interval = setInterval(function() { var xhr = new XMLHttpRequest(); xhr.open('HEAD', params.download_url, false); try { xhr.send(); if (xhr.status === 404) { clearInterval(interval); odoo.rpc('/web/temp_attachment/delete', {attachment_id: params.attachment_id}); } } catch(e) { clearInterval(interval); } }, 1000); return Promise.resolve(); } return this._super(action, options); } }); return {}; });
在模块views/assets.xml中引入该JS:
<odoo> <template id="assets_backend" name="your_module assets" inherit_id="web.assets_backend"> <xpath expr="." position="inside"> <script type="text/javascript" src="/your_module/static/src/js/download_handler.js"></script> </xpath> </template> </odoo>
方法3:直接返回二进制数据,不创建ir.attachment
从根源避免冗余数据,无需删除操作,是最优解。
步骤1:创建返回文件流的控制器
from odoo import http from odoo.http import request import base64 class DownloadController(http.Controller): @http.route('/web/download/zip_file', type='http', auth='user') def download_zip(self, model, res_id): # 根据model和res_id获取数据生成zip_data(替换为你的生成逻辑) record = request.env[model].browse(int(res_id)) zip_data = record.generate_zip_data() # 返回二进制响应 return request.make_response( base64.b64decode(base64.b64encode(zip_data).decode()), headers=[ ('Content-Type', 'application/zip'), ('Content-Disposition', f'attachment; filename="{file_name}"'), ] )
步骤2:修改调用逻辑,直接跳转控制器URL
return { 'type': 'ir.actions.act_url', 'url': f'/web/download/zip_file?model={self._name}&res_id={self.id}', 'target': 'self', }
内容的提问来源于stack exchange,提问作者Makis
相关产品推荐
相关产品推荐

