Jinja Undefined error when using methods on float - jinja2

I have a dictionary object. I can iterate through it using:
{% for key,val in my_dict.items() %}
String: {{ my_dict[key]['my_string'] }}<br>
Float: {{ val['my_float'] }}
{% endfor %}
This will give me something like:
String: I'm a string
Float: 0.72000001
If I try to use a method like:
{% for key,val in my_dict.items() %}
String: {{ my_dict[key]['my_string'] }}<br>
Float: {{ val['my_float']|round(2) }}
{% endfor %}
or
{% for key,val in my_dict.items() %}
String: {{ my_dict[key]['my_string'] }}<br>
Float: {{ "%.2f"|format(val['my_float']) }}
{% endfor %}
I get jinja2.exceptions.UndefinedError: 'dict object' has no attribute 'my_float'
I get a similar error when I call val['my_list'][0], even though I can see it is a list when I just use {{ val['my_list'] }} as it give me something like:
[4.91, 10.16, 19.53, 21.09, 34.27, 52.11, 9.93]
This only seems to be happening with floats and I have no idea why.
I am using jinja2 v3.0.3.

Related

How to dynamically call dbt macros using jinja?

I have a use case where I would like to define the name of a macro and then apply it to one column.
A simplified example could be as follows. I have two macros defined that I want to call dynamically in my model (both take one column as an input):
cast_to_string
convert_empty_string_to_null_value
Now, I want to call them dynamically. See the example below
{%- set macro_names = ["cast_to_string", "convert_empty_string_to_null_value"] -%}
select
{% for macro_name in macro_names %}
-- this should dynamically be evaluated to `{{ cast_to_string(my_column) }}`
-- and `{{ convert_empty_string_to_null_value(my_column) }}`
{{ macro_name(my_column) }}
{% endfor %}
from my_model
However, this will throw an error saying that a string is not callable.
I also tried using {% raw %} {{ {% endraw %} to escape brackets, but that didn’t work either.
So, my question is, if there is a way to dynamically call macros in jinja/dbt?
I think it should work if you remove the quotes :
{%- set macro_names = [cast_to_string, convert_empty_string_to_null_value] -%}
So that jinja doesn't interpret it as string and you can use it as a Callable
I achieve it using this example :
{%- set macro_names = [print_lower, print_upper] -%}
{% for macro_name in macro_names %}
{{ macro_name("test") }}
{% endfor %}
and
{% macro print_lower(string) %}
{{ print(string|lower) }}
{% endmacro %}
{% macro print_upper(string) %}
{{ print(string|upper) }}
{% endmacro %}

Listing all the key-value properites in site.pages

On my blog, I have a debug page which lists several things, including all the properties for each page in site.pages, like so:
{%- for page in site.pages -%}
<strong>{{ page.path }}:</strong><br>
<table>
{% for key_value in page %}
{% if key_value[0] == "content" %}
<tr><td>content</td><td>[{{ key_value[1] | size }} characters]</td></tr>
{% else %}
<tr><td>{{ key_value[0] }}</td><td><span>{{ key_value[1] }}</span></td></tr>
{% endif %}
{% endfor %}
</table>
{%- endfor -%}
This produces output like this:
So far, so good.
Now, I want to do the exact same thing with site.posts, but it doesn't work.
In particular, key_value[0] and key_value[1] don't have any value. The iteration does produce something, but it is not a key an value.
For example, the following produces a list of key names:
{%- for pp in site.posts -%}
<strong>{{ pp.path }}:</strong><br>
{% for kv in pp %}
{% capture cap %}{{ kv }}{% endcapture %}
<div>{{ cap }}</div>
{% endfor %}
<br>
{%- endfor -%}
like so:
I can't seem to get the content of the properties in a generic way, however. I guess the type of the things in the site.posts is not the same as sites.pages, in particular not a simple liquid dictionary.
I've tried messing around with to_liquid mentioned here without luck.

Add class to double curly brackets

i am using the double curly brackets to import variables into my html from my python code. I was inquiring on how to add class to these curly brackets in order to modify the variable in css. Here is a piece of code I am working on.
<div>
{{ form.username.label(class ='username-label') }}
<div>
{% if form.username.errors %}
{{ form.username }}
<div>
{% for error in form.username.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.username }}
{% endif %}
</div>
I tried to add class into the form.username.label in order to change the appearance of the label but it doesnt work. Is there a way to do it. I want it specifically for that label and not the whole div. I hope the only solution is not to add another div tag to the variable
Let's see, whether I get you correctly.
{{ form.username.label(class_='username-label') }}
should become
{{ form.username.label(class_='username-label with-errors') }}
in case the form is invalid.
I'd approach it with
<div>
{% if form.username.errors %}
{{ form.username.label(class ='username-label with-errors') }}
{% else %}
{{ form.username.label(class ='username-label') }}
{% endif %}
<div>
{% if form.username.errors %}
{{ form.username }}
<div>
{% for error in form.username.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.username }}
{% endif %}
</div>
</div>
Also you could use actual <label> and an <ul> for improving semantics.
Edit: Added underscores, so class_ is applied in a Flask template correctly.

Correct syntax for addressing JSON object in Jekyll _data file

I am creating a page in Jekyll and attempting to use data in a JSON file stored in Jekyll's "_data" folder. The JSON file is "/_data/objectsandproperties.json" and contains:
{
"objectA": { "propertyA": "FooA", "propertyB": "BarA" },
"objectB": { "propertyA": "FooB", "propertyB": "BarB" },
"objectC": { "propertyA": "FooC", "propertyB": "BarC" }
}
I would like to output a list formatted like this:
<dl>
<dt>objectA</dt>
<dd>propertyA: FooA</dd>
<dd>propertyB: BarA</dd>
<dt>objectB</dt>
<dd>propertyA: FooB</dd>
<dd>propertyB: BarB</dd>
<dt>objectC</dt>
<dd>propertyA: FooC</dd>
<dd>propertyB: BarC</dd>
</dl>
I am currently using Liquid tags in my markdown file like this:
{% for objects in site.data.objectsandproperties %}
<dl>
{% for object in objects %}
<dt>Object names: {{ object }}</dt>
<dd>propertyA: {{ object.propertyA }}</dd>
<dd>propertyB: {{ object.propertyB }}</dd>
{% endfor %}
</dl>
{% endfor %}
This is not working as the object is not "objectA" but the entire objectA object with properties etc.
I don't have access to the script that creates the JSON file so I cannot add a label or make it an array etc. I'm hoping I can get this to work with Liquid.
Thanks.
Using the inspect filter to better understand what we are working with :
{% for object in site.data.objectsandproperties %}
{{ object | inspect }}
>> returns an array like :
>> ["objectA", {"propertyA"=>"FooA", "propertyB"=>"BarA"}]
name : {{ object.first }} or {{ object[0] }}
properties : {{ object.last}} or {{ object[1] }}
{% endfor %}
Your code can look like :
<dl>
{% for object in site.data.objectsandproperties %}
<dt>Object names: {{ object | first }}</dt>
<dd>propertyA: {{ object.last.propertyA }}</dd>
<dd>propertyB: {{ object.last.propertyB }}</dd>
{% endfor %}
</dl>
You can access and print keys of data by utilizing the attributes of the forloop object:
<dl>
{%- for object in site.data.objectsandproperties %}
{%- for prop in object %}
{%- if forloop.first %}
<dt>{{ prop }}</dt>
{%- else %}
{%- for subprop in prop %}
<dd>{{ subprop[0] }}: {{ subprop[1] }}</dd>
{%- endfor %}
{%- endif %}
{%- endfor %}
{%- endfor %}
</dl>

Nested "for" loop inside "if" condition in Liquid/Jekyll

I'm getting syntax error while trying to generate site. Do you have any ideas what can be wrong here?
Error: Liquid syntax error: Unknown tag 'elsif'
{% if page.title == "Tags" %}
{% for tag in site.tags %}
{% elsif page.title == "Categories" %}
{% for tag in site.categories %}
{% endif %}
{{ tag[0] }}
{% endfor %}
You can't start a loop conditionally like that, control blocks must be properly nested. To accomplish what you're trying to do you could do:
{% if page.title == "Tags" %}
{% assign data = site.tags %}
{% elsif page.title == "Categories" %}
{% assign data = site.categories %}
{% endif %}
{% for tag in data %}
{{ tag[0] }}
{% endfor %}
You have got it all wrong. The nested loops does not work this way.
It should start and end inside the same conditional.
if conditional
for loop
endfor
endif
something like this.
So, the correct way to do it should be this
{% if page.title == "Tags" %}
{% for tag in site.tags %}
{{ tag[0] }}
{% endfor %}
{% elsif page.title == "Categories" %}
{% for tag in site.categories %}
{{ tag[0] }}
{% endfor %}
{% endif %}
This code should do exactly what you want correctly but there is a better approach as answered by JJJ