Search code examples
jinja2

How to loop on a variable to might be either a string or a list?


I am using https://cryptic-cliffs-32040.herokuapp.com/ Jinja live parser. I am expecting the below to print just allow 30.0.0.0/24, in one line, following the else path.

Template

{% if prefixes[1] is defined %}
{%- for p in prefixes -%}
allow {{p}}
{% endfor -%}
{% else %}
allow {{prefixes}}
{% endif %}`

{# I am expecting the below to print just allow 30.0.0.0/24 in one line by taking the else #} 
{% if subnets[1] is defined %} 
{%- for s in subnets -%}
allow {{s}}
{% endfor -%}
{% else %}
allow {{subnets}}
{% endif %}

Values

{
    "prefixes": ["10.0.0.0/24", "20.0.0.0/24"],
    "subnets": "30.0.0.0/24"
}

Render

allow•10.0.0.0/24
allow•20.0.0.0/24


allow•3
allow•0
allow•.
allow•0
allow•.
allow•0
allow•.
allow•0
allow•/
allow•2
allow•4

I was expecting to see

allow•10.0.0.0/24
allow•20.0.0.0/24

allow•30.0.0.0/24

Solution

  • Your actual test was a good idea, but Python treats string as iterables, which is a great feature of the language, to be honest. So 'abc'[1], is a valid statement that would actually give you the first character of the string, in this case, a.

    One way to overcome this is to normalize your input with an inline if:

    prefixes if prefixes is not string else [prefixes]
    

    This would normalize it to, either the list contained into prefixes, or a brand new list of the string contained in prefixes.

    And then, you can feed this inline if into your loops:

    {%- for prefix in (prefixes if prefixes is not string else [prefixes]) -%}
      allow {{ prefix }}
    {% endfor %}
    
    {% for subnet in (subnets if subnets is not string else [subnets]) -%}
      allow {{ subnet }}
    {% endfor %}
    

    Wich renders your expected:

    allow 10.0.0.0/24 
    allow 20.0.0.0/24 
    
    
    allow 30.0.0.0/24