Taking a list from dictionary in Python and dynamically exploding it to YAML using Jinja2 template?

Question:

I am trying to populate a YAML file with values from a dictionary in python.

from jinja2 import Environment, FileSystemLoader
import glob

def map_template(context,template_path,template_name,destination):

    environment = Environment(loader = FileSystemLoader(template_path),   trim_blocks=True, lstrip_blocks=True)
    results_filename = destination
    results_template = environment.get_template(template_name)
    with open(results_filename, mode="w", encoding="utf-8") as results:
        results.write(results_template.render(context))
        print(f"... wrote {results_filename}")

The value in the context looks like

 {'node' : {'value' : [{'key1':val1,'key2':"str2",'key3':val3}] } }

The wanted output YAML looks like

node:
     - key1:val1
       key2:"str2"
       key3: val3

My current solution in my template

node:
      - {{node.value[0].key1}}
        {{node.value[0].key2}}
        {{node.value[0].key3}}

But is there a way to dynamically explode the list using jinja2 template without having to hardcode the number of elements and their names?

Asked By: Kspr

||

Answers:

One complicating factor here is that your data structure doesn’t match your desired output. Rather than trying to produce YAML through a templating process, you should first generate the appropriate data structure. If you have something like:

data = {
    "node": {
        "value": [
            {"key1": "val1", "key2": "str2", "key3": "val3"},
        ]
    }
}
context = {"node_value": {"node": data["node"]["value"]}}

Then you could register a custom to_yaml filter that would use the yaml module to produce the desired output:

from jinja2 import Environment, FileSystemLoader
import yaml


def to_yaml(val):
    return yaml.safe_dump(val)


data = {
    "node": {
        "value": [
            {"key1": "val1", "key2": "str2", "key3": "val3"},
        ]
    }
}
context = {"node_value": {"node": data["node"]["value"]}}


environment = Environment(
    loader=FileSystemLoader("."), trim_blocks=True, lstrip_blocks=True
)
environment.filters["to_yaml"] = to_yaml

results_template = environment.get_template("example.j2")
with open("example.txt", mode="w", encoding="utf-8") as results:
    results.write(results_template.render(context))

Given this content in example.j2:

{{ node_value|to_yaml }}

The above code produces the following example.txt:

node:
- key1: val1
  key2: str2
  key3: val3

If for some reason you can’t modify your context value, you could do something like this instead in your template:

node:
{{ node.value|to_yaml }}

Which will also produce:

node:
- key1: val1
  key2: str2
  key3: val3

And of course you can use things like the indent filter if you want the content shift over as in your question.

Answered By: larsks
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.