Generate YAML using Jinja2 Templates in Python

In this tutorial, you’ll learn how to use Jinja2 templates to generate YAML files dynamically in Python.

Jinja2 is a templating engine that allows you to create reusable templates for various file formats, including YAML.

 

 

Create Jinja2 Templates for YAML

To use Jinja2 for YAML generation, you must install the Jinja2 library and create a template with placeholders.

You can install Jinja2 using pip:

!pip install Jinja2

Now, let’s create a simple YAML template using Jinja2 placeholders:

from jinja2 import Template
yaml_template = """
name: {{ name }}
age: {{ age }}
city: {{ city }}
"""
template = Template(yaml_template)
rendered_yaml = template.render(name="Amr", age=30, city="Cairo")
print(rendered_yaml)

Output:

name: Amr
age: 30
city: Cairo

The template uses double curly braces {{ }} to define placeholders for variables.

When rendering the template, you pass the values for these variables, and Jinja2 replaces the placeholders with the provided data.

 

Variables and Expressions in Jinja2

You can define and use variables directly within Jinja2 templates:

from jinja2 import Template
yaml_template = """
{% set greeting = "Marhaban" %}
message: {{ greeting }}, {{ name }}!
details:
  age: {{ age }}
  city: {{ city }}
"""
template = Template(yaml_template)
rendered_yaml = template.render(name="Fatma", age=25, city="Alexandria")
print(rendered_yaml)

Output:

message: Marhaban, Fatma!
details:
  age: 25
  city: Alexandria

The {% set %} directive allows you to define variables within the template itself.

These variables can then be used alongside the context variables passed during rendering.

Expressions and math operations

Jinja2 supports various expressions and math operations within templates:

from jinja2 import Template
yaml_template = """
product:
  name: {{ product_name }}
  price: {{ price }}
  discounted_price: {{ price * 0.9 }}
  tax: {{ price * 0.1 }}
  total: {{ price * 1.1 }}
"""
template = Template(yaml_template)
rendered_yaml = template.render(product_name="Egyptian Cotton Sheets", price=100)
print(rendered_yaml)

Output:

product:
  name: Egyptian Cotton Sheets
  price: 100
  discounted_price: 90.0
  tax: 10.0
  total: 110.0

Using default values and variable evaluation

Jinja2 provides ways to set default values and evaluate variables:

from jinja2 import Template
yaml_template = """
user:
  name: {{ name | default("Guest") }}
  premium: {{ premium | default(false) }}
  visits: {{ visits | default(0) }}
  status: {{ "Active" if visits > 5 else "Inactive" }}
"""
template = Template(yaml_template)
rendered_yaml = template.render(name="Hossam", visits=7)
print(rendered_yaml)

Output:

user:
  name: Hossam
  premium: false
  visits: 7
  status: Active

The default filter sets a fallback value if the variable is not provided.

The if expression allows for conditional evaluation within the template.

 

Control Structures in Templates

Jinja2 supports conditional statements for dynamic content generation.

The if, elif, and else statements allow you to create conditional blocks in your templates.

from jinja2 import Template
yaml_template = """
user:
  name: {{ name }}
  age: {{ age }}
  status: {% if age < 18 %}
    minor
  {% elif age < 65 %}
    adult
  {% else %}
    senior
  {% endif %}
"""
template = Template(yaml_template)
rendered_yaml = template.render(name="Nour", age=30)
print(rendered_yaml)

Output:

user:
  name: Nour
  age: 30
  status: adult

For loops

You can use for loops to iterate over lists and dictionaries:

from jinja2 import Template
yaml_template = """
team:
  name: {{ team_name }}
  members: {% for member in members %}
    - name: {{ member.name }}
      role: {{ member.role }}  {% endfor %}
"""
team_data = {
    "team_name": "Pyramid Builders",
    "members": [
        {"name": "Amira", "role": "Architect"},
        {"name": "Karim", "role": "Engineer"},
        {"name": "Laila", "role": "Project Manager"}
    ]
}
template = Template(yaml_template)
rendered_yaml = template.render(**team_data)
print(rendered_yaml)

Output:

team:
  name: Pyramid Builders
  members:
    - name: Amira
      role: Architect
    - name: Karim
      role: Engineer
    - name: Laila
      role: Project Manager

The for loop iterates over the members list, creating a YAML list item for each member with their name and role.

Nested control structures

You can combine loops and conditionals for more complex structures:

from jinja2 import Template
yaml_template = """departments: {% for dept, employees in departments.items() %}
  {{ dept }}:    {% for employee in employees %}
    - name: {{ employee.name }}      {% if employee.age >= 30 %}
      senior: true      {% endif %}
      skills:        {% for skill in employee.skills %}
        - {{ skill }}        {% endfor %}    {% endfor %} {% endfor %}"""
data = {
    "departments": {
        "IT": [
            {"name": "Amir", "age": 35, "skills": ["Python", "JavaScript"]},
            {"name": "Dina", "age": 28, "skills": ["Java", "SQL"]}
        ],
        "HR": [
            {"name": "Yasmin", "age": 40, "skills": ["Recruitment", "Training"]}
        ]
    }
}
template = Template(yaml_template)
rendered_yaml = template.render(**data)
print(rendered_yaml)

Output:

departments: 
  IT:    
    - name: Amir      
      senior: true      
      skills:        
        - Python        
        - JavaScript            
    - name: Dina      
      skills:        
        - Java        
        - SQL             
  HR:    
    - name: Yasmin      
      senior: true      
      skills:        
        - Recruitment        
        - Training    

 

Apply built-in filters

Jinja2 provides various built-in filters to manipulate data.

Filters like upper, replace, capitalize, float, round, and lower are used to transform the input data within the template.

from jinja2 import Template
yaml_template = """product:
  name: {{ name | upper }}
  description: {{ description | replace("_", " ") | capitalize }}
  price: {{ price | float | round(2) }}
  tags:  {% for tag in tags %}
    - {{ tag | lower }}{% endfor %}"""
data = {
    "name": "Egyptian Papyrus",
    "description": "authentic_ancient_writing_material",
    "price": 29.99999,
    "tags": ["HISTORICAL", "Artifact", "Collectible"]
}
template = Template(yaml_template)
rendered_yaml = template.render(**data)
print(rendered_yaml)

Output:

product:
  name: EGYPTIAN PAPYRUS
  description: Authentic ancient writing material
  price: 30.0
  tags:  
    - historical
    - artifact
    - collectible

 

Using include to embed templates

You can use the include statement to embed one template within another:

from jinja2 import Environment, FileSystemLoader

# Create a Jinja2 environment
env = Environment(loader=FileSystemLoader('.'))

# Define the main template
main_template = """
main_config:
  {% include 'sub_template.yaml' %}
additional_info:
  created_by: {{ author }}
"""

# Define the sub-template
sub_template = """
  name: {{ name }}
  version: {{ version }}
"""

# Write the sub-template to a file
with open('sub_template.yaml', 'w') as f:
    f.write(sub_template)

# Render the main template
template = env.from_string(main_template)
rendered_yaml = template.render(name="MyApp", version="1.0", author="Mahmoud")
print(rendered_yaml)
import os
os.remove('sub_template.yaml')

Output:

main_config:
  name: MyApp
  version: 1.0
additional_info:
  created_by: Mahmoud

 

Import template code and macros

Jinja2 allows you to import macros and template code.

The import statement allows you to reuse macros and template code across multiple templates:

from jinja2 import Environment, FileSystemLoader

# Create a Jinja2 environment
env = Environment(loader=FileSystemLoader('.'))

# Define a macro in a separate file
macro_template = """
{% macro render_person(name, age) -%}
  name: {{ name }}
  age: {{ age }}
{%- endmacro %}
"""

# Write the macro to a file
with open('macros.yaml', 'w') as f:
    f.write(macro_template)

# Main template that imports and uses the macro
main_template = """
{% import 'macros.yaml' as macros %}
people:
- {{ macros.render_person('Amina', 28) }}
- {{ macros.render_person('Hassan', 35) }}
"""

template = env.from_string(main_template)
rendered_yaml = template.render()
print(rendered_yaml)
import os
os.remove('macros.yaml')

Output:

people:
- name: Amina
  age: 28
- name: Hassan
  age: 35
Leave a Reply

Your email address will not be published. Required fields are marked *