jinja2 和 python-docx-template 光速入门
有根据模板去渲染和打印 word 的需求,因此研究一个。本来是想用 HTML 渲染再打印的,但发现用 word 更方便。
python-docx-template 是 python 的一个 docx 模板引擎库,它依赖 python-docx
去读写 docx 文件内容,依赖jinja2
进行模板渲染。它就像 Java 中的poi-tl
(恰巧我最后一个需求刚用到它)。
这里也说了——它依赖 jinja2 模板引擎,所以需要学一下 jinja2 的语法。
安装
1 |
|
jinja2
jinja2 是一个文本模板引擎,它一般上是用来生成 html 的,但并不和 html 绑定,因此它不像 vue 那样,模板语法也是合法 html,而是像 poi-tl 那样(原谅我没学过其他的模板引擎),模板和内容解耦。
jinja2 这么使用(虽然我不会这么直接用它啦):
1 |
|
接下来,快速把所有玩意儿都过一遍!
首先,有三种基础语法,是容易理解的:
{{ ... }}
:插值表达式{% ... %}
:控制结构- ``:注释
插值
jinja2 支持插值任意合法 Python 表达式(有一定限制):
1 |
|
注意——user.name
等价于user['name']
,jinja 在这方面不关心索引访问和 getattr 访问(当然,肯定是按照一个优先级顺序去访问的),就像 js 一样,这个很方便——你传 dataclass 还是传 dict,一样的用。
测试器
上面说 jinja2 支持任意 python 表达式?不全是,比如这里提出一个示例——jinja2 不支持 python 的 is 操作符(实际上我们在实践中,is 完全是用来判 None 的,所以在模板引擎中这个行为基本无意义)。jinja2 重新定义 is——is 的右边是所谓的测试器,测试器是可以自己定义的,这里不表。示例见下:
1 |
|
jinja2 的 is 同样有两种形式——is 和 is not。
下面是常用测试器:
name | desc |
---|---|
none | is None |
string | 类型是 str |
number | 类型是 int 或 float |
sequence | 序列(非字符串) |
sameas(value) | 对应 python 的 is |
equalto(value) | 对应 ==,jinja2 有==,但如果配置不容忍 undefined,==遇上未定义会报错,这里不会 |
defined | 是否已定义(变量是否存在) |
… | 就这些足够用啦,还需要咋样? |
过滤器
jinja2 支持所谓的 过滤器 Filter:|
是管道,就像 unix 的管道,这里的意思是为 name 使用 trim 过滤器,然后再使用 capitalize 过滤器。过滤器实际上是 python 中定义的函数,我们自己也可以定义过滤器,但这里不表。
1 |
|
内置的过滤器有:
name | desc |
---|---|
default(value) | 默认值,注意用法:{{ name \| default('Anonymous') }} ,它行为是一个柯里化函数 |
escape / safe | safe 表示不转义 html,默认似乎是不转义的(可配置),escape 表示转义 |
join(delimiter) | 字面意思,用 delimiter join 一个集合 |
truncate(length) | 把太长的字符串截断,尾部显示 ... |
… | 过火了,剩下的有需求看文档,本来就不该在模板中塞太多业务逻辑,仅供显示用 |
宏
jinja2 支持 宏 Macros,宏就像函数,你给它参数,它给你结果。宏必须先定义后调用:
1 |
|
控制结构
对于一个模板引擎,只需要两种控制结构,if 和 for。
条件逻辑
同 python 的,注意需要一个 endif 块表示结尾,for 也是如此。
1 |
|
循环
循环和 python 的区别在于,循环中有一个内置的 loop 变量,每轮循环时做更新:
- loop.index:下标,以 1 开头
- loop.index0:下标,以 0 开头
- loop.first, loop.last:是否是第一项,是否是最后一项
- loop.revindex:剩余项数,为
length - loop.index0
1 |
|
这里的 item
也是可以被解构的,但这里不表。
变量定义
有两种变量定义——set 和 with,都是容易理解的:set 的作用域是整个模板,除非它定义在 macro 中,with 的作用域是 with 块内。注意 set 能使用 python 的解包语法,而 with 则更加……传统,但方便。
1 |
|
怎么说呢?jinja2 学完这些感觉就足够啦!回到 docxtpl。
docxtpl
先来个 HelloWorld:
1 |
|
一些扩展,一些坑
docxtpl 对 jinja2 做了一些更严格的限制和扩展。
关于限制:jinja2 的 tag 内容,必须处在同一个段落的同一个 run 中,即 {{ name }}
这整个内容必须处在同一个 run 中,且不能换行。
run,指的是 word 中为同一个格式的内容的文字的集合,就像 html 和 markdown 中的各种标签。这是可以想见的——必须要“批处理”以减少重复和浪费。而像{{ name }}
这样的标签如果跨多个 run,docxtpl 就会迷糊——我究竟用前面的 run 还是后面的 run 呢?
这里有一个重要的坑:像这样 `
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 协议 ,转载请注明出处!