如何在 Jinja2 自定义过滤器中使用 Python 生成器

Posted

技术标签:

【中文标题】如何在 Jinja2 自定义过滤器中使用 Python 生成器【英文标题】:How to use Python Generator in Jinja2 Custom Filter 【发布时间】:2020-07-19 00:10:35 【问题描述】:

我需要生成一个带有字母索引的文档,比如这个:

Channels:
 - A: Foobar item
 - B: Foobaz item
 - ...

我有带有FoobarFoobaz 等的输入文件,我希望jinja2 从模板文件中使用索引AB 等生成它,如下所示:

Channels: % for item in items %
  -  None | next_id :  item.name  item % endfor %

我想用这个模板使用 Python Generator,但是我找不到可行的解决方案,最新的代码版本是:

...
# Simple letters generator
def idgen():
    value = 'A'
    while True:
        yield value
        value = (chr(ord(value)+1))

gen = idgen()

# Function to be used as Custom Filter
# https://jinja.palletsprojects.com/en/master/api/#writing-filters
# I don't know is it implementable without function, just with generator 
def next_id():
    return next(gen)

env = Environment(loader=FileSystemLoader(template_dir))
env.filters['next_id'] = next_id
template = env.get_template(template_filename)

# items = ['name': 'Foobar', 'name': 'Foobaz']

print(next_id()) # To see if generator iterates
print(template.render(items=items))
print(next_id())

对应的输出是:

B
Channels: 
  - A: Foobar item 
  - A: Foobaz item 

C

需要你的帮助,蜂群。

【问题讨论】:

我需要在模板 None | next_id 中发送None,因为 Jinja API 需要它。这是添加包装函数的两个原因之一。 【参考方案1】:

到目前为止,我只找到了一种解决方法。假设 Jinja 根据自定义过滤器的功能输入使用某种缓存/优化:

这里:

B
Channels: 
  - A: Foobar item 
  - A: Foobaz item 
C

所以最明显的解决方法是每次都发送新的输入,例如 loop.index | next_id :

模板:

Channels: % for item in items %
  -  loop.index | next_id :  item.name  item % endfor %

结果:

Channels: 
  - A: Foobar item 
  - B: Foobaz item  

【讨论】:

以上是关于如何在 Jinja2 自定义过滤器中使用 Python 生成器的主要内容,如果未能解决你的问题,请参考以下文章

ansible中过滤器的介绍以及如何自定义过滤器

02-模板(过滤器,控制代码块)

在 jinja2 中使用 django 过滤器

Flask教程11模板

Flask阶段回顾和展望

如何调试 Jinja2 模板?