I'm using Jinja2 with YAML and have the following structure:
{%- set example = [ (20, "on"), (40, "off")] %}
- name: example_yaml
loop:
{%- for value, state in example %}
- TheNumber: {{ value }}
TheState: {{ state }}
{%- endfor %}
When the first loop is rendered, TheNumber is correct with 20, but TheState ends up being True. I've looked through the documentation and have tried adding a string filter like this:
{{ state | string }}
But that did not work either. I have also Tried switching the string "on" to something else like "StateShouldBeOn" just to test with. With that I get what I expect TheState = "StateShouldBeOn".
My question is, why is it that "on" renders to a boolean value?
Try to use 'on' instead of "on". That should help.
Related
Is it possible to access the key names along with key values from a yaml file using liquid?
For example from a .yaml file that looks like this:
- type: a
id: b
author: c
website: d
Have liquid code that looks something like this:
{%- for item in YAMLFILE -%}
{{ item.??? }} = {{ item.??? }}
{%- endfor -%}
with the following output:
type = a
id = b
author = c
website = d
I am trying to do this this way because my yaml file has different key names along with values and it would be a pain having to embed a bunch of different if else statements to account for every possible key name.
Thank you all so much!
Assuming your yaml file is in _data/test, you can use:
{% for list in site.data.test %}
{% for item in list %}
{{ item[0] }} = {{ item[1] }}
{% endfor %}
{% endfor %}
item[0] is the key and item[1] is the value. It will work with different number of keys and names in each list.
Also be aware that four spaces in Markdown formats the content of the second for loop as a code block. Simply do not indent if putting this code in a Markdown file.
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"']
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.
I have read a number of examples of how to re-write the when clause to avoid the warning about jinja2 template delimiters {{ and }} - but have not seen any on avoid use of {% for ... %}.
I have roughly the following step:
- name: Wait for Started status to clear
uri:
url: http://{{ container.ip }}:8080/upgrade/api/v1/upgrades/{{ theuuid }}
return_content: yes
register: progress_status
until: >
{% for c in (progress_status.content | from_json).data.states %}
{{ c.state != 'Started' }}
{% if not loop.last %}and{% endif %}
{% endfor %}
retries: 30
delay: 15
The uri call returns a list of containers and their 'state' in a json form. I want to keep querying this uri until none of the containers are in the Started state.
This generates the warning due to the {% for ... %} loop.
I could do a less specific test by look at the entire response for the json string representing a state of Started. But that seems more cryptic than looking at the state of each container (building up a boolean expression of true and true and ...). But this is my alternative for now.
Or I could use a block and do the query, and then build the condition as a fact, and then use the fact as the until on the block. But that too seems harder to read.
Suggestions for how to handle this sort of pattern?
You can use selectattr/rejectattr filters to fetch/delete specific items from lists:
until: (progress_status.content | from_json).data.states | rejectattr('state','equalto','Started') | list | length == 0
Here we take (progress_status.content | from_json).data.states list, remove all elements that have state=='Started' and check length of remaining list.
Task will complete when the list after reject is empty meaning all items have Started state.
I'm using Jekyll to create a new blog. It uses Liquid underneath.
Jekyll defines certain "variables": site, content, page, post and paginator. These "variables" have several "members". For instance, post.date will return the date of a post, while post.url will return its url.
My question is: can I access a variable's member using another variable as the member name?
See the following example:
{% if my_condition %}
{% assign name = 'date' %}
{% else %}
{% assign name = 'url' %}
{% endif %}
I have a variable called name which is either 'date' or 'url'.
How can I make the liquid equivalent of post[name] in ruby?
The only way I've found is using a for loop to iterate over all the pairs (key-value) of post. Beware! It is quite horrible:
{% for property in post %}
{% if property[0] == name %}
{{ property[1] }}
{% endif %}
{% endfor %}
Argh! I hope there is a better way.
Thanks.
I don't know what I was thinking.
post[name] is a perfectly valid liquid construction. So the for-if code above can be replaced by this:
{{ post[name] }}
I thought that I tried this, but apparently I didn't. D'oh!
Liquid admits even fancier constructs; the following one is syntactically correct, and will return the expected value if post, element, categories, etc are correctly defined:
{{ post[element.id].categories[1].name }}
I am greatly surprised with Liquid. Will definitively continue investigating.
Actually, this did not work for me. I tried a bunch of different combinations and what finally worked was
<!-- object myObj.test has the string value "this is a test" -->
{% assign x = 'test' %}
{{ myObj.[x] }}
Output:
this is a test