Search code examples
pythonjinja2generatorgrafanadashboard

How to use Python Generator in Jinja2 Custom Filter


I need to generate a document with letter indices, like this one:

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

I have input file with Foobar, Foobaz, etc and I want jinja2 to generate it with indeces A, B, etc from template file, like this:

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

I want to use Python Generator with this template, but I can't find the working solution, the latest code version is:

...
# 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())

And corresponding output is:

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

C

Need your help, hivemind.


Solution

  • I found just one workaround so far. Assume that Jinja uses some kind of caching/optimization depending on Custom Filter's function input:

    Here:

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

    So the most obvious workaround is to send new input every time, i.g. {{ loop.index | next_id }}:

    Template:

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

    Result:

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