感谢:Odoo中利用python的Docxtpl模块生成word附件 - 灰信网(软件开发博客聚合) (freesion.com)这篇文章那个,我在做任务的时候这篇文章给我了一条生路。
现在我对word打印任务做个总结。
首先,需要做一个需要打印的word模块,上连接:Python 使用DocxTemplate模板实现将数据写入word中 - NoName-Newbee - 博客园 (cnblogs.com),我看很多方法中都是直接在电脑本地拿模板,我需要做的是在系统中拿到这个模板,所以需要将做好的word模板上传到系统的附件中(设置-技术-附件,点击创建按钮,上传你的文件即可)
odoo中大部分打印都是在动作旁边的打印按钮,点击之后走.xml文件中的template然后打印出pdf格式的文件,但是我不会用这个方法打印word格式的文件,所以我就将打印做成了按钮放在页面上,让他执行.py文件中的方法,关键就是.py文件中的方法。
下载docxtpl依赖包,这里就不说明了,我使用的是0.16.4版本。
在方法中的第一步就是拿到系统中上传的附件模板,这里就很好拿了attachment = self.env['ir.attachment'].search([('name','=','XXXXXX')]),系统中的附件都都存在ir.attachment模型中然后根据他的name拿上传的数据附件就是其中的datas字段,但是拿到附件(attacment.datas)之后这里根本就是一堆乱码这样没法做到渲染,这就需要解码,attachment_datas=base.b64decode(attachment.datas)(这里需要引进依赖包import base64),attachment_datas是解码之后的数据,但是解码之后也不能直接渲染,因为这个数据也是一堆乱码不是一个word文件,所以我们需要把乱码存到虚拟目录中。
创建虚拟目录:with tempfile.TemporaryDirectory() as tmpdirname:我这里报错了所以我在这一行的下边做了个print(tmpdirname),然后docx_path=tmpdirname + str(uuid.uuid4())+'.docx',docx_path就是虚拟目录的路径,uuid是生成一串随机数据具体功能忘记了反正就是为了防止路径重复,需要引入依赖吧(import uuid)。
做了虚拟目录之后将解码的文件发放到虚拟目录中:with open(docx_path ,'wb') af f :f.write(attachment.datas),这样就把解码的文件放进生成的虚拟目录中了,可以做修改,根据虚拟目录路径拿到附件模板:in_stream = DocxTemplate(docx_path),拿到模板之后就可以对模板进行数据渲染。
举个例子,我在模板中有一个需要渲染的字段{{name}},在后端方法中就可以写成context={'name':‘狗尾巴草的可怜’},然后in_stream.reder(context),就是对in_stream进行渲染。
渲染完成之后怎么把完成的word文件取出来?
我的方法:在创建一个虚拟目录:file_dir=tmpdirname + str(uuid.uuid4())+'检测单.docx',将渲染的模块保存到虚拟目录中in_stream.save(file_dir),保存到虚拟目录中之后怎么拿到?当然是读取目录了:with open(file_dir,'rb') ad f :byte_read = f.read(),byte_read就是读取出来的文件
将读取出来的文件再次报错到系统的附件中test=self.env['ir.attachment'].create({''name“:"报错附件的名称","datas":base64.base64encode(byte_read),"res_id":self.id,"res_model":" XXXX"})
解释一波:name就是保存附件的名称;datas是附件,我们拿出来的时候转码了,这次存进去当然也要转回去;res_id是打印当前数据的id;res_model是当前打印的模型,保存到附件之后当然可以直接下载了,下载代码如下图:
解释一波: type固定写法,如果下载的话需要这个type类型;targe是在当前窗口哦下载还是新建一个窗口;url就是%后边不一样,他需要这个附件的id,之前不是已经create了吗,只需要放到这里就可以。
补充:我在做打印的时候需要将图片也打印上,在word模板文档中只需要一个{{logo}},这个代替即可。然后就是对logo的渲染。
首先需要达到图片,例如公司的logo(self.company.logo),将拿到的logo图片解码base64.b64decode(self.company.logo),将解码的图片放到虚拟目录中,创建虚拟目录img_path=tmpdirname+str(uuid.uuid4())+'.png',然后放入虚拟目录中with.open(img_path,'wb') as f:f.write(img_data),放入虚拟目录后就开始渲染,context['logo']=InlineImage(in_stream,im_path,height=Mm(20)),分析一波:InlineImage是外部依赖包的方法需要引入(from docxtpl import InlineImage),里面有三个参数in_stream是模板存储的地址,im_path是图片的虚拟地址,height是要求渲染上的图片的高度,这好像可以不写(不清楚),Mm也需要引入外部依赖吧(from docx.shared import Mm),这样就可以将图片渲染上,注意这一步要放到render方法之前啊,别因为我写在了后边就放到后边。
上一波代码截图: