jinja2: substituting variables into a for loop statement - for-loop

Let's say I have the following python dictionary:
d = { "name0": "Johnny",
"name1": "Stephanie",
"name2": "Jake",
"name3": "Nicole",
"name4": "Cody"
}
I'm passing this dictionary to a jinja2 template using flask. How can I iterate over these names in the template? Here's what I have so far:
{% for i in range(5) %}
{% for name in d.name{{ i }} %}
{{ name }}
{% endfor %}
{% endfor %}
I know this isn't the best way to do this (this example simplifies my data to the extreme). Best way would probably be to put all the names in a list called "names" and embed that within the dictionary like so: d = { "names": ["Johnny", Stephanie", etc.] }. I'm wondering if my specific question has a solution.

Iterate over the dictionary like you can in python:
{% for k, v in d.items() %}
{{ v }}
{% endfor %}

Related

Remove outside double quotes from list of jinja strings

I'm formatting & appending strings to a list but the ref() function (used in dbt) isn't a string so I need to remove the outside quotes while retaining the inner single quotes.
I've attempted to chain strip() and replace() but neither are solving the problem.
Current code:
{% for result in results_list %}
{% set result = "ref('{}')".format(result) %}
{{ ref_format_results.append(result) }}
{% endfor %}
{% do log(ref_format_results, info=true) %}
Current output:
["ref('model_a')","ref('model_b')","ref('model_c')"]
Desired output:
[ref('model_a'),ref('model_b'),ref('model_c')]
You're creating a string, which needs to be delimited somehow, and will always show outer quotes when printed/logged.
If you're templating those values into another file or string, then you'll already have what you want.
{% for r in ref_format_results %}
select * from {{ "{{" ~ r ~ "}}" }};
{% endfor %}
will produce:
select * from {{ ref('model_a') }};
select * from {{ ref('model_b') }};
select * from {{ ref('model_c') }};
But more importantly, why set result to a string in the first place? Are you trying to delay the execution of the ref() macro? If not, this will work just fine, to give you a list of evaluated refs (which are Relations):
{% for result in results_list %}
{% set result = ref(result) %}
{{ ref_format_results.append(result) }}
{% endfor %}
{% do log(ref_format_results, info=true) %}
Outputs:
['"my_db"."my_schema"."model_a"', '"my_db"."my_schema"."model_b"', '"my_db"."my_schema"."model_c"']

ansible jinja2 - how to use "loop range" output in other "for statement"

I want to use the n values in the next for statement and I expect output as router_0, router_1. But any optisons like router_[n], router_{{ n }}, router_(n) etc didnt work. How can we do this?
{% for n in range(0, 2) %}
{% for rtr in web.router_[n] %}
{% if rtr.interface.type == 'lacp' %}
interface Port-Channel{{ rtr.interface.id }}
.
{% endif %}
{% endfor %}
{% endfor %}
In vars.yaml, there are variables for paired routers, router_0 and router_1. I want to generate the config template for both routers at the same time.
Thanks,
Concatenate the name of the attribute
{% for rtr in web['router_' ~ n] %}

How can one generate a dict in Jinja/Ansible as a var?

I need to dynamically generate a dict and assign it to an Ansible var so that the same data is available to every host and does not need to be regenerated on each one. This will be looped over by a task and template. It is possible to build lists by nesting Jinja in a var definition like so:
var_list: >
[
{% set data = [] %}
{% for host in groups['all'] %}
{{ data.append(host) }}
{% endfor %}
{{ data }}
]
However, I am not able to do so with dicts:
var_dict: >
{
{% set data = dict() %}
{% for host in groups['all'] %}
{{ data | combine({host: []}) }}
{% endfor %}
{{ data }}
}
This results in a giant, garbled string: {u'host.subdom.dom.tld': []} {}...
What I am expecting is a var set to a true dict object that can have its components referenced individually (like {{ var_dict.host1[0] }}), with a structure similar to the following JSON:
{
"host1": [],
"host2": []
}
Is there a way to cause this to expand into a proper dict when assigned to the variable? Or is there perhaps another way to generate a dict from a loop without that awful set_fact hackery?
Either of your templates is broken (the first one produces nested lists, the second one prints data in loop with this expression {{ data | combine({host: []}) }}, the value of data remains empty until the end).
Jinja2 is a templating engine, you don't really need to create data structures to print them later. You can form your output directly.
For the list:
var_list: >
[ {% for host in groups['all'] %}'{{ host }}',{% endfor %} ]
For the dictionary:
var_dict: >
{ {% for host in groups['all'] %}'{{ host }}': [],{% endfor %} }
As you expect the values to be interpreted by Ansible, you don't need to pay attention to the trailing coma, otherwise there is loop.last.

Jinja2 Templating: Conditionally forming a set from Ansible Variables

I am trying to generate some config using Jinja2 templating and Ansible variables. The framework under which I am currently working does not allow me to perform the following operation in Ansible and thus I was hoping to achieve the same results in Jinja2.
My Ansible variables are as follows:
---
Top:
inner:
type: type1
other_random_variable:
- random: 1
inner2:
type: type2
inner3:
type: type1
The above structure works well when I am iterating over a loop and forming a configuration file as follows:
{% if Top is defined %}
{% for inner_vars in Top %}
# perform substitution here
{% endfor %}
{% endif %}
What I would like to do is to form a set of types such that I can generate another configuration for each unique type.
Is there any way for me to iterate through Top and add an item to a set?
I think I have a solution that could work:
{% set types = [] %}
{% if Top is defined%}
{% for inner_var in Top %}
{% if types.append(Top[inner_var].type) %}{% endif %}
{% endfor %}
{% endif %}
{{ types|unique }}

Capturing Item Index in Collection

I was wondering what is the proper way of finding the index of an item in an array is in a Liquid template, and selected related items based off of the index. Currently I'm able to calculate the value, but it seems to be a string and I can't then find other items in the array with the string. For example in a CMS:
{% for site_page in site.pages.all %}
{% if site_page.id == page.id %}
{% assign page_index = forloop.index0 %}
{% capture previous_page_index %}
{{ page_index | minus: 1 }}
{% endcapture %}
{% break %}
{% endif %}
{% endfor %}
The expected value can be found in previous_page_index (in this case 0) however, if i try to do something like site.pages.all[previous_page_index] I receive no output. If I do the same thing with a hardcoded index value: site.pages.all[0] it does yield an output. Does anyone have an idea/example on how this is supposed to be done in liquid?
Best I can figure is to use {% for item in array limit:1 offset:forloop.index0 %}. For example:
require 'liquid'
chars = %w[a b c]
names = %w[alpha bravo charlie]
puts Liquid::Template.parse(<<DONE).render( 'chars'=>chars, 'names'=>names )
{% for c in chars %}
{{c}} is
{% for n in names limit:1 offset:forloop.index0 %}{{n}}{% endfor %}
{% endfor %}
DONE
…which produces…
a is
alpha
b is
bravo
c is
charlie
Editorial aside: ouch. What an ugly tempting language. I understand its goals, but the burden it places on users is heinous.

Resources