在web开发过程中,经常需要导出一些word文档,比如运营月报、进货单、收据等。它们的特点是格式都是一致的,只是需要不同的数据进行填充。
docx 和 docxtpl 是比较常见的处理 word 文件的python库。其中前者用于创建一个包含段落、图片、表格、页眉等元素文档,后者使用类似 jinja2 的方式从模板文档生成新的文档。
docx是一种压缩文档格式,使用XML形式定义了样式、段落等内容。详情可以查看 《An Informal Introduction to DOCX》。
文档构建
安装方式
使用 pip 安装这两个库即可:
| 12
 
 | pip install python-docxpip install docxtpl
 
 | 
其中 docxtpl 还依赖 jinja2 库。
创建 word
使用以下的方式创建一个 Document 对象,之后的操作都是基于该对象。
| 12
 3
 
 | from docx import Document
 document = Document()
 
 | 
使用 add_* 函数添加到该文档对象,包括:
| 函数 | 说明 | 
| add_paragraph | 段落 | 
| add_heading | 标题 | 
| add_page_break | 换页符 | 
| add_table(rows, cols) | 表格 | 
| add_picture | 图片 | 
| add_run | 字符片段 | 
run 是word中包含相同样式的字符片段,通常它的级别比 paragraph 更低。
这是正常的文字。这是粗体文字。 这是正常的文字。
 比如上述的一段文字。依次由3个 run 组成。
待文档构建完成后,可以使用 save 函数保存到文件中。
| 1
 | document.save('demo.docx')
 | 
文档模板
使用方法
docxtpl 的使用方法和 jinja2 类似。
| 12
 3
 4
 5
 6
 
 | from docxtpl import DocxTemplate
 doc = DocxTemplate("my_word_template.docx")
 context = { 'company_name' : "World company" }
 doc.render(context)
 doc.save("generated_doc.docx")
 
 | 
docxtpl 的核心在于前端标签定义。
变量渲染
就像 jinja2 中一样,使用两个大括号即可。
当 var 是 RichText 对象时,必须使用以下的方式:
为了能够准确的区分以上两种情况,建议变量名称不要单独命名为 r ,。
存储和输出
整合Flask
将文档对象保存到新建的 BytesIO() 实例即可,但需要注意的是在返回给浏览器前将文件指针重新指向开头。
详情查看 flask.send_file 的用法。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | import io
 from docxtpl import DocxTemplate
 from flask import send_file
 
 def export_monthly_report(year, index):
 context = {}
 doc = DocxTemplate(app.root_path + '/templates/report_monthly.docx')
 target_filename = 'monthly_{}_{}.docx'.format(year, index)
 doc.render(context)
 file_stream = io.BytesIO()
 doc.save(file_stream)
 file_stream.seek(0)
 return send_file(file_stream, as_attachment=True, attachment_filename=target_filename)
 
 | 
整合Django
和 flask类似。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | def export_monthly_report(request, year, index):context = {}
 doc = DocxTemplate(app.root_path + '/templates/report_monthly.docx')
 target_filename = 'monthly_{}_{}.docx'.format(year, index)
 doc.render(context)
 file_stream = io.BytesIO()
 doc.save(file_stream)
 file_stream.seek(0)
 response = HttpResponse(
 file_stream.getvalue(),
 content_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
 )
 response["Content-Disposition"] = 'attachment; filename = "{}"'.format(target_filename)
 response["Content-Encoding"] = "UTF-8"
 return response
 
 |