Python-docx-template是一个功能强大的Word文档自动化生成库,它基于模板引擎的设计思想,允许用户通过编写模板与Python代码逻辑分离的方式,高效生成结构复杂、样式多样的Word文档。在文章python-docx-template模板化Word文档生成指北介绍该库的基础用法之上,本文将进一步结合官方示例,提供多个实用场景的代码解析与拓展,涵盖复杂样式、自定义过滤器、嵌套循环及富文本渲染等高级功能,以提升文档生成的效率与灵活性。下文将分模块展开具体示例与实现。
python-docx-template的官方代码仓库地址为:python-docx-template[1],详细文档可参阅:python-docx-template doc[2]。

本文使用的python-docx-template版本为0.20.2,安装命令如下:
❝pip install docxtpl
本示例用于生成包含富文本样式与单元格背景色的Word表格文档。
模板内容:

渲染代码:
# python-docx-template/blob/master/tests/comments.pyfrom docxtpl import DocxTemplate, RichText# data: python-docx-template/blob/master/tests/templates/cellbg_tpl.docxtpl = DocxTemplate("templates/cellbg_tpl.docx")context = {"alerts": [ {"date": "2015-03-10","desc": RichText("Very critical alert", color="FF0000", bold=True),"type": "CRITICAL","bg": "FF0000", }, {"date": "2015-03-11","desc": RichText("Just a warning"),"type": "WARNING","bg": "FFDD00", } ],}tpl.render(context)tpl.save("output/cellbg.docx")本示例主要介绍通过自定义Jinja2过滤器实现动态数据渲染,Jinja2模板中过滤器的核心格式为:{{ 变量名|过滤器名(参数1, 参数2, ...) }},其中:
|(竖线)是过滤器的分隔符,左侧是要处理的变量,右侧是过滤器名称;()内是传给过滤器函数的参数(无参数时可省略括号);{{ base_value_float|my_filterB(2) }} 中,base_value_float是变量,my_filterB是过滤器名,2是传递的参数。模板内容:

渲染代码:
# python-docx-template/blob/master/tests/custom_jinja_filters.pyfrom docxtpl import DocxTemplateimport jinja2# 创建jinja2环境对象,用于管理模板渲染的配置jinja_env = jinja2.Environment()# 自定义过滤器函数defmy_filterA(value, my_string_arg):# 将原始值和参数字符串拼接,中间加空格 return_value = value + " " + my_string_argreturn return_valuedefmy_filterB(value, my_float_arg):# 将原始值和参数数值相加 return_value = value + my_float_argreturn return_value# 将自定义过滤器注册到jinja2环境中,使其能在模板中被调用# 注册后在Word模板中可通过{{ 变量名| my_filterA('参数') }}形式使用jinja_env.filters["my_filterA"] = my_filterAjinja_env.filters["my_filterB"] = my_filterBcontext = {"base_value_string": " Hello", "base_value_float": 1.5}# data: python-docx-template/blob/master/tests/templates/custom_jinja_filters_tpl.docxtpl = DocxTemplate("templates/custom_jinja_filters_tpl.docx")tpl.render(context, jinja_env)tpl.save("output/custom_jinja_filters.docx")以下代码展示了如何渲染子Word文档,替换主Word文档中嵌入的各类文件,填充数据后保存文档:
# python-docx-template/blob/master/tests/embedded.pyfrom docxtpl import DocxTemplate# 加载内嵌子模板文件# data: python-docx-template/blob/master/tests/templates/embedded_embedded_docx_tpl.docxembedded_docx_tpl = DocxTemplate("templates/embedded_embedded_docx_tpl.docx")# 定义模板渲染的上下文数据context = {"name": "John Doe", # 要填充到模板中的姓名值}embedded_docx_tpl.render(context)# 保存渲染后的子模板到指定路径,供后续主模板调用embedded_docx_tpl.save("output/embedded_embedded_docx.docx")# 加载主模板文件tpl = DocxTemplate("templates/embedded_main_tpl.docx")# 定义主模板的上下文数据context = {"name": "John Doe",}# 替换主模板中嵌入的Word文档# 参数1:模原本嵌入的占位文件路径# 参数2:要替换成的目标文件路径tpl.replace_embedded("templates/embedded_dummy.docx", "templates/embedded_static_docx.docx")tpl.replace_embedded("templates/embedded_dummy2.docx", "output/embedded_embedded_docx.docx")# 说明:docx 本质是 zip 压缩包,嵌入的文件会存储在word/embeddings/目录下tpl.replace_zipname("word/embeddings/Feuille_Microsoft_Office_Excel3.xlsx", "templates/real_Excel.xlsx"# 要替换成的实际文件路径)tpl.replace_zipname("word/embeddings/Pr_sentation_Microsoft_Office_PowerPoint4.pptx","templates/real_PowerPoint.pptx")tpl.render(context)tpl.save("output/embedded.docx")本示例展示了在自动转义模式下,将包含XML特殊字符、Unicode文本和动态键值对的上下文数据渲染到模板中。
模板内容:
渲染代码:
# python-docx-template/blob/master/tests/escape_auto.pyimport osfrom unicodedata import namefrom docxtpl import DocxTemplateXML_RESERVED = """<"&'>"""# data: python-docx-template/blob/master/tests/templates/escape_tpl_auto.docxtpl = DocxTemplate("templates/escape_tpl_auto.docx")context = {"nested_dict": {name(str(c)): c for c in XML_RESERVED},"autoescape": 'Escaped "str & ing"!',"autoescape_unicode": "This is an escaped <unicode> example \u4f60 & \u6211","iteritems": lambda x: x.items(),}# autoescape=True表示自动转义tpl.render(context, autoescape=True)OUTPUT = "output"ifnot os.path.exists(OUTPUT): os.makedirs(OUTPUT)tpl.save(OUTPUT + "/escape_auto.docx")实际上iteritems(nested_dict)就是调用渲染定义的lambda函数,把nested_dict传进去,拿到它的所有键值对:
{% for k, v in iteritems(nested_dict) %}{{ k.capitalize() }}: {{ v }}{% endfor %}也可以在Jinja2模板中使用Python表达式,直接调用字典的item方法:
{% for k, v in nested_dict.items() %}{{ k.capitalize() }}: {{ v }}{% endfor %}以下示例说明如何替换Word模板文档(包含页眉页脚)中的图片,并演示如何将处理后的文档分别通过常规方式和内存文件对象保存为本地文件:
# python-docx-template/blob/master/tests/header_footer_image_file_obj.pyfrom docxtpl import DocxTemplateimport io# 定义两个输出文档的路径和文件名DEST_FILE = "output/header_footer_image_file_obj.docx"DEST_FILE2 = "output/header_footer_image_file_obj2.docx"# data: python-docx-template/blob/master/tests/templates/header_footer_image_tpl.docxtpl = DocxTemplate("templates/header_footer_image_tpl.docx")context = {"mycompany": "The World Wide company", }# 读取模板中需要被替换的图片文件,并转换为内存字节流对象dummy_pic = io.BytesIO(open("templates/dummy_pic_for_header.png", "rb").read())# 读取新的替换图片文件(python.png),并转换为内存字节流对象new_image = io.BytesIO(open("templates/python.png", "rb").read())# 将dummy_pic对应的图片替换为new_image对应的图片tpl.replace_media(dummy_pic, new_image)tpl.render(context)tpl.save(DEST_FILE)tpl = DocxTemplate("templates/header_footer_image_tpl.docx")# 将内存中的图片字节流指针重置到起始位置dummy_pic.seek(0)new_image.seek(0)# 再次执行图片替换操作tpl.replace_media(dummy_pic, new_image)# 再次渲染模板变量tpl.render(context)# 创建一个空的内存字节流对象,用于临时存储文档内容file_obj = io.BytesIO()# 将处理后的文档保存到内存字节流对象中tpl.save(file_obj)# 将内存字节流指针重置到起始位置,准备读取内容file_obj.seek(0)# 以二进制写入模式打开第二个输出文件,将内存中的文档内容写入文件with open(DEST_FILE2, "wb") as f: f.write(file_obj.read())dummy_pic.close()new_image.close()上述代码实现图片替换并非基于文件名,而是基于二进制内容的匹配。这是因为replace_media方法根据图片的二进制内容来识别图像,而非依赖文件名或在Word中显示的名称。 由于Word文档 (.docx) 本质上是一个压缩包,其中的图片以二进制形式存储在word/media/目录下,且在某些Word版本中,图片文件名可能被自动重命名(例如改为 image1.png),与原始文件名无关。
注意🚀:待替换图片尺寸不宜过大,且需关闭Word模板的图片压缩功能;否则Word会自动压缩模板中的图片,改变其二进制数据,最终导致图片替换操作失败。
以下示例展示了直接在命令行中使用docxtpl模块,基于模板文件和作为上下文数据的JSON文件生成docx文档:
# python-docx-template/blob/master/tests/module_execute.pyimport os# data: python-docx-template/blob/master/tests/templates/module_execute_tpl.docxTEMPLATE_PATH = "templates/module_execute_tpl.docx"# 存储需要填充到模板中的数据JSON_PATH = "templates/module_execute.json"OUTPUT_FILENAME = "output/module_execute.docx"# docxtpl命令参数:强制覆盖已存在的输出文件OVERWRITE = "-o"# docxtpl命令参数:静默模式执行,不输出额外日志信息QUIET = "-q"# 删除已存在的输出文件if os.path.exists(OUTPUT_FILENAME): os.unlink(OUTPUT_FILENAME) # 切换工作目录到当前脚本所在的目录os.chdir(os.path.dirname(__file__))# 通过Python模块方式调用docxtpl,传入模板、数据、输出路径和参数# 可通过python -m docxtpl -help查看调用帮助cmd = "python -m docxtpl %s %s %s %s %s" % ( TEMPLATE_PATH, # 模板文件路径 JSON_PATH, # 数据文件路径 OUTPUT_FILENAME, # 输出文件路径 OVERWRITE, # 覆盖参数 QUIET # 静默参数)print('Executing "%s" ...' % cmd)os.system(cmd)if os.path.exists(OUTPUT_FILENAME): print(" --> File %s has been generated." % OUTPUT_FILENAME)以下示例展示了如何通过模板语法实现逐层循环渲染,最终生成包含这些嵌套数据的Word文档:
# python-docx-template/blob/master/tests/nested_for.pyfrom docxtpl import DocxTemplate# data: python-docx-template/blob/master/tests/templates/nested_for_tpl.docxtpl = DocxTemplate("templates/nested_for_tpl.docx")context = {"dishes": [ {"name": "Pizza", "ingredients": ["bread", "tomato", "ham"]}, {"name": "Hamburger","ingredients": ["bread", "chopped steak", "cheese"], }, ],"authors": [ {"name": "Saint-Exupery","books": [ {"title": "Le petit prince"}, {"title": "L'aviateur"}, ], }, {"name": "Barjavel","books": [ {"title": "Ravage"}, {"title": "La nuit des temps"}, ], }, ],}tpl.render(context)tpl.save("output/nested_for.docx")若字体显示异常,通常是由于字体仅适配了特定文字区域。解决方法是在字体名前加上区域标识和冒号(如 eastAsia:微软雅黑),从而指定文字的区域渲染方式。如果不清楚区域标识,也可解压模板文件后分析document.xml确认字体对应的区域。常见区域标识包括:
以下代码展示了如何设置不同的东亚字体:
# python-docx-template/blob/master/tests/richtext_eastAsia.pyfrom docxtpl import DocxTemplate, RichText# data: python-docx-template/blob/master/tests/templates/richtext_eastAsia_tpl.docxtpl = DocxTemplate("templates/richtext_eastAsia_tpl.docx")# 2. 创建富文本对象,分别设置不同的东亚字体# eastAsia: 前缀表示该字体设置仅作用于东亚字符(中文、日文、韩文等)rt = RichText("测试TEST", font="eastAsia:Microsoft YaHei")ch = RichText("测试TEST", font="eastAsia:微软雅黑")sun = RichText("测试TEST", font="eastAsia:SimSun")context = {"example": rt,"Chinese": ch,"simsun": sun,}tpl.render(context)tpl.save("output/richtext_eastAsia.docx")Python-docx-template的核心功能是基于Jinja2语法动态生成Word文档。其RichText类进一步增强了灵活性,允许直接以编程方式插入格式丰富的文本,而无需为每种样式组合单独设置模板变量。RichText对象可在初始化时直接传入文本,通过多次调用add方法可向其追加不同格式的文本片段。最后,将该对象整体赋给模板上下文中的变量。在Word模板中,只需使用{{ rich_text_var }}引用该变量,即可渲染成包含多种格式的连续段落。

add方法是构建RichText对象的核心,其所有参数均用于控制当前操作所添加文本的格式:
❝重要提示🚩:style(段落样式)参数比较特殊。它通常只在第一次调用add方法时有效,后续调用中再设置style通常会被忽略。
以下是一段使用示例代码,按顺序介绍RichText.add()方法的各个参数:
from docxtpl import DocxTemplate, RichTextimport os# 1. 创建模板对象并初始化RichTextdoc = DocxTemplate("template.docx")rt = RichText()# 2. 添加基础文本(必需参数)rt.add("这是普通文本")# 3. 设置段落样式(影响整个段落)rt.add("\n标题文本", style="Heading1")# 4. 设置字体颜色rt.add(" 红色文字", color="#FF0000")rt.add(" 蓝色文字", color="blue")# 5. 设置背景高亮rt.add(" 黄底文字", highlight="yellow")# 6. 设置字体大小(单位:磅)rt.add(" 小号字", size=8)rt.add(" 大号字", size=20)# 7. 上下标设置rt.add(" 正常文字")rt.add(" 上标", superscript=True)rt.add(" 下标", subscript=True)# 8. 字体样式rt.add(" 加粗", bold=True)rt.add(" 倾斜", italic=True)rt.add(" 下划线", underline=True)rt.add(" 删除线", strike=True)# 9. 字体设置rt.add(" 宋体", font="eastAsia:SimSun")rt.add(" 微软雅黑", font="eastAsia:Microsoft YaHei")# 10. 超链接(需要先生成链接ID)url_id = doc.build_url_id("https://www.example.com")rt.add(" 超链接文本", url_id=url_id)# 11. 文字方向rt.add(" 正常方向")rt.add(" 从右向左文字", rtl=True) # 中文设置无效果# 12. 语言设置rt.add(" English text", lang="en-US")rt.add(" 中文文本", lang="zh-CN")context = {'rich_text_var': rt}doc.render(context)os.makedirs('output',exist_ok=True)doc.save("output/generated_document.docx")在模板中只需简单引用:
{{ rich_text_var }}TemplateError类是Jinja2模板引擎中所有模板相关异常的基类,在python-docx-template中专门用于捕获模板渲染过程中出现的各类错误。当使用tpl.render()渲染 Word 模板时,以下情况会抛出TemplateError异常:
{{ name }},但render只传了test_variable);{% endif %}、变量引用格式错误);{{ test_variable | xxx }},xxx不是 Jinja2 内置过滤器)。以下代码展示了如何测捕获Word模板渲染时的TemplateError异常,并打印详细的错误信息:
from docxtpl import DocxTemplatefrom jinja2.exceptions import TemplateErrorprint("=" * 50)print("正在生成测试用的模板错误")print("." * 50)try: tpl = DocxTemplate("templates/template_error_tpl.docx")# 如果模板中存在语法错误或变量缺失,会触发TemplateError异常 tpl.render({"test_variable": "测试变量值"})# 捕获模板渲染过程中出现的所有TemplateError异常except TemplateError as the_error:# 打印错误的基本描述信息 print(f"模板渲染错误:{str(the_error)}")# 检查异常对象是否包含docx_context属性if hasattr(the_error, "docx_context"):# 打印上下文信息的标题 print("错误上下文详情:")# 遍历并打印错误上下文的每一行内容for line in the_error.docx_context: print(line)# 确保tpl变量存在时再执行保存操作if'tpl'in locals():# 将渲染后的文档保存到指定路径 tpl.save("output/template_error.docx") print(f"文档已保存至:output/template_error.docx")print("." * 50)print(" 模板错误测试完成 ")print("=" * 50)欢迎关注我的公众号“彭彭加油鸭”,原创技术文章第一时间推送。

python-docx-template: https://github.com/elapouya/python-docx-template
[2]python-docx-template doc: https://docxtpl.readthedocs.io/