odoo 后台与前端文件(附件)存储与下载实现
笔记太多了很乱,想想还是写博客的好,慢慢更
当然了,前提是你已经配好了odoo开发环境
一、odoo后台界面实现附件的上传和下载
1)、在应用中搜索下图组件,安装
成功后,随便打开一个form视图就能看到上面多了一个附件按钮,点击即可上传
2)、在你的模型中加入一个关联到这个附件模型的字段
information_attachment = fields.Many2many('ir.attachment', compute='_get_attachment_ids', string=u'附件')
这里使用的是Many2many进行关联
ir.attachment :就是上图安装的模块Attachments……的模型的_name,这是odoo本身自带的附件模型功能,odoo后台上传的所有附件都会存到这个模型的数据库表里
compute :根据所属的模型和实例获取附件
既然如此,所有的附件都存在同一个模型中,如何知道那些附件是由哪个模型中的哪个实例上传的呢?
我们先来看看数据库中ir.attachement的表
字段太多了不一一解释
res_model: 定义的模型名
res_id:模型名实例化的对象id
有个这两个字段,我们就能取到对应模型对面的特例(实例化对象)
而_get_attachment_ids方法就是用来获取附件
1 def _get_attachment_ids(self): 2 att_model = self.env['ir.attachment'] #获取附件模型 3 for obj in self: 4 query = [('res_model', '=', self._name), ('res_id', '=', obj.id)] #根据res_model和res_id查询附件 5 obj.information_attachment = att_model.search(query) #取得附件list
根据上面代码取得附件,便可以显示出来了。
3)、值得一提的是:
<field name="information_attachment" widget="many2many_binary"/>
在form视图xml文件中指定widget为 “many2many_binary”,则会如下图显示,更加直观
点击相应的附件,即可下载。
一点都不可爱的分割线
二、前端请求服务器,获取附件
odoo后台实现了附件的上传和下载,那我的前端页面要怎么获取它呢?
换句话说,我的controller要怎么写呢?
1)、首先,前端取得你要获取附件的res_model和res_id,传回给后台附件id。
根据前面的讲解,根据这两个字段可以取到一个附件list,要取得特定的附件,我还需要一个附件id
attachment_id = request.env['ir.attachment'].sudo().search_read( [('res_model', '=', 'em.council.information'), ('res_id', '=', task_id)], ["name", "id"]
这段代码,是在controller根据模型的res_model和res_id取得附件list,我存下了以下两个字段:
name : 就是附件的名字 #用于前端的展示,给用户显示附件名称 id:每个附件有一个特定的id #唯一标示,在数据库中查询附件用
然后给前端返回去,至于怎么取到数据,就看你喜欢了,我用的是jinja2,ajax也可以
如果是jinjia2,就返回
return template.render(data=attachment_id)
如果是ajax,自然就是
Response(json.dumps(attachment_id), 200)
然后你自己在前端中把他们渲染出来展示给用户
2)、用户点击相应附件,实现下载
用户点击了附件,我们有一个附件id,我们这里叫attachment_id,跟前面的attachment_id[1]存的id是一个东西
1 function onReturnAttachment_id(attachment_id) { 2 window.location.href = 'http://localhost:8069/w/download?attachment_id=' + attachment_id 3 }
为了方便讲解,前端偷懒直接用了跳转,请你不要偷懒,写一个阿贾克斯。
上面的跳转已经暴露了我的路由是
w/download
然后传回了attachment_id给controller,接下来就是:
1、controller取得attachment_id 2、controller根据attachment_id查询数据库找到相应的附件 3、controller给前端返回附件
直接上代码:
1 @http.route('/w/download', type='http', auth='public', csrf=False) 2 def w_download_attachment(self, **kwargs): 3 attachment_id = kwargs.get('attachment_id') 4 attachment = request.env['ir.attachment'].sudo().search_read( 5 [('id', '=', int(attachment_id))], 6 ["name", "datas", "res_model", "res_id", "type", "url"] 7 ) 8 if attachment: 9 attachment = attachment[0] 10 else: 11 return redirect('/w/download') 12 13 res_id = attachment['res_id'] 14 if attachment["type"] == "url": 15 if attachment["url"]: 16 return redirect(attachment["url"]) 17 else: 18 return request.not_found() 19 elif attachment["datas"]: 20 data = StringIO(base64.standard_b64decode(attachment["datas"])) 21 return http.send_file(data, filename=attachment['name'], as_attachment=True) 22 else: 23 return request.not_found()
稍微解释一下:
attachment_id = kwargs.get('attachment_id') #取得前端传回来的id #根据id取得数据库中对应的附件,其中datas就是我们的附件数据 attachment = request.env['ir.attachment'].sudo().search_read( [('id', '=', int(attachment_id))], ["name", "datas", "res_model", "res_id", "type", "url"] )
判断一下有没有取到,取到了又因为search_read返回一个list,但应该只有一条数据,因此 attachment = attachment[0]
if attachment: attachment = attachment[0] else: return redirect('/w/download')
1)、判断一下是不是存的url,是的话重定向
2)、如果不是url存的,取得datas,base64解码一下存到data
3)、把data和文件名打包返回给前端
1 if attachment["type"] == "url": 2 if attachment["url"]: 3 return redirect(attachment["url"]) 4 else: 5 return request.not_found() 6 elif attachment["datas"]: 7 data = StringIO(base64.standard_b64decode(attachment["datas"])) 8 return http.send_file(data, filename=attachment['name'], as_attachment=True) 9 else: 10 return request.not_found()
此时前端访问前面给的url便能下载对应附件了,比如我要下载前面存的附件中的“收藏的前端资料网址”,我理论上访问的是
http://localhost:8069/w/download?attachment_id=266
三、前端上传附件存到数据库中
这个记录一下思路,其实就是一个逆过程,自己尝试一下
1、拿到必要的字段,如res_model和res_id,你要存入附件的datas(base64编码格式) 2、将数据传回controller 3、controller处理数据,并操作数据库create一条记录