PySimpleGUI结合docxtpl渲染Word时提示'Check'未定义报错
问题现象
开发基于PySimpleGUI + docxtpl的住宿发票录入生成工具时,程序运行触发Jinja2模板变量未定义错误:
- 终端运行报错:
File "<template>", line 1, in top-level template code jinja2.exceptions.UndefinedError: 'Check' is undefined
- 调试模式报错:
Exception has occurred: UndefinedError 'Check' is undefined
原实现代码如下:
import PySimpleGUI as sg from pathlib import Path import datetime from docxtpl import DocxTemplate dokumen_path = Path(__file__).parent / "word_template.docx" doc = DocxTemplate(dokumen_path) def tanggal_waktu(): hari = datetime.datetime.today() return hari layout = [ [sg.Text("GUEST NAME"), sg.Input(key="NAMA", do_not_clear=False)], [sg.Text("CHECK-IN"), sg.Input(key="Check-in", do_not_clear=False)], [sg.Text("CHECK-OUT"), sg.Input(key="Check-out", do_not_clear=False)], [sg.Text("ROOM NO"), sg.Input(key="KAMAR",do_not_clear=False)], [sg.Text("TYPE"), sg.Input(key="TIPE",do_not_clear=False)], [sg.Text("PERSON"), sg.Input(key="ORANG", do_not_clear=False)], [sg.Text("ROOM CHARGE"), sg.Input(key="RC",do_not_clear=False),], [sg.Button("Submit"), sg.Exit()], ] window = sg.Window("Invoice Palm Garden Guest House", layout, element_justification="right") while True: event, values = window.read() if event == sg.WIN_CLOSED or event == "Exit": break if event == "Submit": print(values) values["D/T"] = tanggal_waktu() values["TOTAL"] = int(values["RC"]) doc.render(values) output_path = Path(__file__).parent / f"{values['NAMA']}-Invoice.docx" doc.save(output_path) sg.popup("File Saved") else: pass window.close()
配套使用的Word模板见提供的参考截图。
报错根因
- 错误由Jinja2模板语法解析规则和变量名不兼容导致:代码中给入住、离店输入框设置的key为
Check-in、Check-out,包含横杠-特殊字符。如果在Word模板中直接写{{ Check-in }}这类占位符,Jinja2会把横杠识别为减法运算符,将表达式解析为「变量Check 减去 变量in」,执行时找不到名为Check的变量,就会抛出未定义错误。
修复方案
推荐使用改动最小的方案1:
方案1:修改键名为下划线命名(推荐)
Jinja2原生支持下划线分隔的标识符,无需特殊写法:
- 修改Python代码中layout部分的两个输入框key,把横杠替换为下划线:
# 原代码 [sg.Text("CHECK-IN"), sg.Input(key="Check-in", do_not_clear=False)], [sg.Text("CHECK-OUT"), sg.Input(key="Check-out", do_not_clear=False)], # 修改为 [sg.Text("CHECK-IN"), sg.Input(key="Check_in", do_not_clear=False)], [sg.Text("CHECK-OUT"), sg.Input(key="Check_out", do_not_clear=False)], - 打开Word模板文件,把对应的占位符同步修改:
- 将
{{ Check-in }}替换为{{ Check_in }} - 将
{{ Check-out }}替换为{{ Check_out }}
修改后重新运行程序即可正常渲染生成文档。
- 将
方案2:保留原有键名,调整模板取值语法
如果不想修改Python代码中的key定义,可以调整模板里的变量写法,通过字典键访问的形式绕过运算符解析:
- 把代码中
doc.render(values)修改为doc.render({"form_data": values}),将所有表单值包裹在form_data字典下传入模板 - 修改Word模板中所有占位符,增加
form_data.前缀,对于带横杠的键用方括号取值:- 原
{{ NAMA }}改为{{ form_data.NAMA }} - 原
{{ Check-in }}改为{{ form_data['Check-in'] }} - 原
{{ Check-out }}改为{{ form_data['Check-out'] }} - 其余
KAMAR/TIPE/ORANG/RC/D/T/TOTAL等变量同步增加form_data.前缀即可。
- 原
内容的提问来源于stack exchange,提问作者StressCodingCok
相关产品推荐
相关产品推荐

