突然间,就在一次 Frappe 更新之后,你一打开打印视图或尝试下载 PDF,所有打印格式(或某个特定格式)就立刻报错。错误信息只给出了行号,却没有说明是哪个格式,所以很难看出该修什么。
错误信息
你会看到下面其中一种,可能出现在打印视图的对话框中,也可能出现在 PDF/下载的响应里:
Print Format Error
Error in print format on line 19: 'NoneType' object is not callable
或者是更完整的版本,出现在你从 PDF 下载链接得到的服务器错误页面中:
File "apps/frappe/frappe/templates/print_formats/standard.html", line 19, in top-level template code
{{ render_field(df, doc, no_of_cols) }}
File "env/lib/python3.x/site-packages/jinja2/sandbox.py", line 401, in call
return __context.call(__obj, *args, **kwargs)
TypeError: 'NoneType' object is not callable
...
frappe.exceptions.PrintFormatError: Error in print format on line 19: 'NoneType' object is not callable
还有一种相关的情况,显示的是 KeyError 或其他 Jinja 异常,而不是 NoneType,但同样呈现“所有打印格式都挂了”的模式。原因和下面的修复方法是一样的。
为什么会这样
打印格式是通过 Jinja 渲染的:html 模板通过 Jinja 的沙箱调用 Python 函数并读取文档字段。'NoneType' object is not callable 的意思是 Jinja 试图把某个东西当作函数来调用,但那个东西的值是 None——通常是一个空字段(当 备注 为空时就是 {{ doc.remarks() }}),或者是沙箱无法解析的模板宏。
大多数“更新后所有打印格式都坏了”的报告,都能追溯到 Frappe Framework 本身的一次短暂回归,出在 standard.html 模板上——所有内置打印格式和基于 Builder 的打印格式都共用这个模板。一次核心改动导致其中一个模板宏在两个版本之间的短暂窗口内返回了 None。Frappe 修复这类问题很快(几小时到几天内),并以补丁版本发布修复,但在你的站点升级到那个版本之前,所有打印和 PDF 都会以同样的方式失败。同一个模板上已经不止一次发生过这种情况,所以“先更新看看是否修好了”是合理的第一步,而不是一次性的做法。
如果只有 一个 打印格式失败,其余都正常,那原因就在这个格式本身:某个自定义打印格式(或合作伙伴定制过的格式)的 Jinja 或自定义 HTML 调用了某个字段、doctype 方法或打印模板钩子,而应用更新把它删除了、重命名了,或者对某些文档留空了。
如何修复
- 打开文档,点击打印图标,把 打印格式 切换到另一个(内置格式,或 标准)。如果它能正常渲染,问题就出在你原来用的那个格式上——跳到第 4 步。如果所有格式都失败,问题就在 Frappe 本身——继续第 2 步。
- 阅读 traceback,确认它与本问题相符,并找出失败的位置。在打印视图中,重新选择出错的格式以查看对话框;从 PDF 下载链接(或附带 PDF 的邮件/报表)打开完整的服务器错误页面——那里有完整的 traceback。
最后一行会给出异常名称,如果是打印格式特有的错误,还会给出你模板中的行号:
如果失败发生在后台任务中(定时报表、附带 PDF 的邮件)——也就是没有人能看到对话框——请从 Awesome Bar 打开 错误日志,查找同一文档在相近时间的记录;它带有相同的 traceback。
3. 检查你的应用是否都在匹配的、最新的版本上。打开 帮助 → 关于(或在 bench 目录下运行 bench version),对比 已安装应用 列表:Frappe Framework、你的主应用(ERPNext 等)以及任何自定义应用都应该在同一条版本线上(全是 v15,或全是 v16——不能混用),而且都不应落后于该版本线的最新补丁版本。
如果 Frappe Framework 落后了,就更新它:
bench update
bench --site [site-name] migrate
bench build
bench clear-cache
bench clear-website-cache
即使 bench update 无错误地完成,bench build 和两个清除缓存的命令也很重要——过期的 JS/CSS 构建或缓存的页面会继续显示旧的、损坏的行为。在 Frappe Cloud 上,你不需要自己运行这些:公共(共享)bench 会在每周发布日自动更新;在私有 bench 组上,打开站点仪表盘中的 更新 并应用待处理的更新。
4. 如果只有你自己的自定义打印格式失败,打开它(打印格式 列表,或从打印视图点击 自定义),检查 traceback 指出的那一行。查找某个字段引用——它可能对某些文档为空,却被当作函数调用(用 {{ doc.some_field() }} 而不是 {{ doc.some_field }}),或者从某个模板路径导入的宏被应用更新改掉了。修复模板,或者如果这个格式是在更新之前构建的,就按当前的标准布局重新创建它。
如何预防
- 让 Frappe Framework 和其他应用保持在同一版本线上,并使用该版本线的最新补丁版本——这些故障大多会在某个小版本发布中修复,保持更新即可避免。
- 任何更新之后,在团队重新开始使用站点之前,先用你依赖的每个自定义打印格式各打印一份文档。
如果这样仍不能解决
提交支持工单,并附上:确切的错误文本、打印格式名称、帮助 → 关于中的 Frappe Framework 和应用版本,以及你当时正在打印的文档。如果你有完整的回溯信息(来自服务器错误页面或错误日志),请一并附上——这是支持人员确认这是已知的 Framework 回归问题还是你自己的打印格式问题的最快方式。
相关
- 打印
- 在哪里可以找到标准打印格式