Jinja2 template: output format - python-3.x

i am quite new to jinja2 template and i try to get output data by using jinja template.
data_list=
[
{
"v4-filter": {
"accept_1": [
{
"destination-prefix-list": "v4-future-prefix-list-1"
},
{
"source-prefix-list": "v4-future-prefix-list-2"
},
{
"source-prefix-list": "v4-future-prefix-list-3"
},
{
"destination-port": "80"
},
{
"destination-port": "443"
},
{
"action": "accept"
}
],
"accept_2": [
{
"destination-prefix-list": "v4-future-prefix-list4"
},
{
"source-prefix-list": "v4-future-prefix-list5"
},
{
"action": "accept"}]}}]
My jinja template
{%- for d in data_list -%}
{%- for k,v in d.items() -%}
{%- for term,value in v.items() -%}
term:: {{ term }}
{%- for dict_item in value -%}
{%- for key, value in dict_item.items() -%}
{% if key == "source-prefix-list" %}
source-address:: {{value}}
{% endif %}
{% if key == "destination-prefix-list" %}
destination-address:: {{value}}
{% endif %}
{% if key == "destination-port" %}
destination-port:: {{value}}
{% endif %}
{% if key == "action" %}
action:: {{value}}
{% endif %}
{%- endfor -%}
{%- endfor -%}
{%- endfor -%}
{%- endfor -%}
{%- endfor -%}
Output that i get
term:: accept_1
destination-address:: v4-future-prefix-list-1
source-address:: v4-future-prefix-list-2
source-address:: v4-future-prefix-list-3
destination-port:: 80
destination-port:: 443
action:: accept
term:: accept_2
destination-address:: v4-future-prefix-list4
source-address:: v4-future-prefix-list5
action:: accept
Output that i want
term accept_1 {
source-address:: v4-future-prefix-list-1
v4-future-prefix-list-2
v4-future-prefix-list-3
destination-port:: 80
443
action:: accept
}
term accept_2 {
source-address:: v4-future-prefix-list-5
destination-address:: v4-future-prefix-list-4
action:: accept
}
The jinja template looks not nice i know, but at the end it contains all the information that i need, is just question now to format properly as expected.
I tried by using nested if statement to properly reach the result but with no success.
Any hint is welcome.
Thanks, Pablo.

The following template will produce the requested output:
{%- for d in data_list -%}
{%- for k,v in d.items() -%}
{%- for term,value in v.items() -%}
{%- set label = namespace(source=false, dest=false, port=false) %}
term {{ term }} {
{%- for dict_item in value -%}
{%- for key, value in dict_item.items() -%}
{%- if key == "source-prefix-list" %}
{% if not label.source %}source-address::{% else %}{{ ' '*16 }}{% endif %} {{value}}
{%- set label.source = true %}
{%- endif %}
{%- if key == "destination-prefix-list" %}
{% if not label.dest %}destination-address::{% else %}{{ ' '*21 }}{% endif %} {{value}}
{%- set label.dest = true %}
{%- endif %}
{%- if key == "destination-port" %}
{% if not label.port %}destination-port::{% else %}{{ ' '*18 }}{% endif %} {{value}}
{%- set label.port = true %}
{%- endif %}
{%- if key == "action" %}
action:: {{value}}
{%- endif %}
{%- endfor %}
{%- endfor %}
}
{% endfor %}
{%- endfor %}
{%- endfor %}
Output:
term accept_1 {
destination-address:: v4-future-prefix-list-1
source-address:: v4-future-prefix-list-2
v4-future-prefix-list-3
destination-port:: 80
443
action:: accept
}
term accept_2 {
destination-address:: v4-future-prefix-list4
source-address:: v4-future-prefix-list5
action:: accept
}
Here we use a Jinja2 namespace object that we can use to track whether a label has been already printed or not in the current cycle. If a label is printed, we flip the boolean tracking variable in our label namespace variable and then just print enough space to correctly align the next printed value.

Related

how to remove line break in twig template containing a for loop and an include?

I would like to remove a line break between multiple html tags (<a>) from a twig template which contains a for loop and an include of another twig template.
{% for child in field.value %}
{% include '#EasyAdmin/crud/field/association_link.html.twig' with {
field: {
value: child,},
} %}
{%- if loop.index < field.value|length -%}
,
{%- endif %}
{% endfor %}
And the twig templated included :
{% set url = ea_url()
.setController('App\\Controller\\Admin\\' ~ get_class(field.value)|split('\\')|last ~ 'CrudController')
.setEntityId(field.value.id)
.setAction('edit') %}
{% if url -%}
{{ field.value|markdown }}
{%- else -%}
{{ field.value|markdown }}
{%- endif -%}
It renders such a result :
<a href=url2>name1<a/>
,
<a href=url2>name2<a/>
How to get this result instead ?
<a href=url2>name1<a/>,
<a href=url2>name2<a/>
2nd row, 6th field : a break line before and after the comma

Macro to automatically generate section numbers

I am struggling with creating a macro to automatically generate section number and subsection numbers. I had thought the snippet below would work but it is failing to assign the value if not already set. I am sure this is obvious but my lack of jinja experience is showing... or perhaps lack of Python experience... or both!
I get this error.
{% set sectionnumber.value = sectionnumber.value + 1 %}
jinja2.exceptions.TemplateRuntimeError: cannot assign attribute on non-namespace object
{% macro getsectionnumber(type) -%}
{% if subsectionnumber is none %}
{% if sectionnumber is none %}
{% set sectionnumber = namespace(value=0) %}
{% endif %}
{% set subsectionnumber = namespace(value=0) %}
{% endif %}
{% if type == 'section' %}
{% if sectionnumber is none %}
{% set sectionnumber = namespace(value=0) %}
{% endif %}
{% set sectionnumber.value = sectionnumber.value + 1 %}
{{ sectionnumber.value }}
{% endif %}
{% if type == 'subsection' %}
{% set subsectionnumber.value = subsectionnumber.value + 1 %}
{{ sectionnumber.value }}.{{ subsectionnumber.value }}
{% endif %}
my template:
template = """
{% set sectionnumber = namespace(value=0) %}
{% set subsectionnumber = namespace(value=0) %}
{% macro getsectionnumber(type) -%}
{% if type == 'section' %}
{% set sectionnumber.value = sectionnumber.value + 1 %}
{{ sectionnumber.value }}
{% endif %}
{% if type == 'subsection' %}
{% set subsectionnumber.value = subsectionnumber.value + 1 %}
{{ sectionnumber.value }}.{{ subsectionnumber.value }}
{% endif %}
{% endmacro %}
{{ getsectionnumber('section') }}
{{ getsectionnumber('subsection') }}
{{ getsectionnumber('subsection') }}
{{ getsectionnumber('section') }}
{{ getsectionnumber('subsection') }}
{{ getsectionnumber('subsection') }}
"""
print the template:
print(Template(template).render())
result:
1
1.1
1.2
2
2.3
2.4

How to break “for loop” in Django template

My code is:
{% for key, value in section.items %}
{% for key_t, value_t in title.items %}
{% if value_t.section_id == key|add:"0" %}
<li class="nav-item-header"><div class="text-uppercase font-size-xs line-height-xs">
{{value.title}}</div> <i class="icon-menu" title="Tables"></i></li>
{% endif %}
{% endfor %}
{% endfor %}
I want to break the for loop when if the condition is true. like as
{% for key, value in section.items %}
{% for key_t, value_t in title.items %}
{% if value_t.section_id == key|add:"0" %}
<li class="nav-item-header"><div class="text-uppercase font-size-xs line-height-xs">
{{value.title}}</div> <i class="icon-menu" title="Tables"></i></li>
{{break}}
{% endif %}
{% endfor %}
{% endfor %}
How is it possible? please help me...
There is no way to break out of a for loop in Django Template. However, you can achieve this by setting a variable and adding an if statement on the top like this.
{% set isBreak = False %}
{% for number in numbers %}
{% if 99 == number %}
{% set isBreak = true %}
{% endif %}
{% if isBreak %}
{# this is a comment. Do nothing. #}
{% else %}
<div>{{number}}</div>
{% endif %}
{% endfor %}
for some additional help check out this link
https://dev.to/anuragrana/for-loops-in-django-2jdi
or check this answer on stack overflow
How to break "for loop" in Django template

How to properly use if not statement with twig

I have been trying to figure out how to use the if not clause with Twig template engine.
According to the documents:
You can also use not to check for values that evaluate to false:
So I'm trying to use it like this:
{% if not signed_in %}
You are not signed in.
{% endif %}
And signed_in comes from a middleware. Looking like this:
if (!$this->container->auth->check()) {
/* Render a Sign-up */
return $this->container->view->render($response, 'subscriber/index.twig', ['signed_in' => false]);
}
So if I {{ dump(signed_in) }} when I'm not signed in it returns false. But when I'm signed in, singed_in returns null.
The current solution I'm going with is {% if signed_in is defined %}, but that is actually not what I'm looking for, or is it?
Twig != is a comparison operator.
The following comparison operators are supported in any expression: ==, !=, <, >, >=, and <=
You can use != to compare any two things that php lets you compare, and what you get back is a boolean.
Twig not is a logic operator. (so are and and or)
not: Negates a statement.
I recommend you to use:
{% if signed_in == true %} or {% if signed_in == false%}
This is the twig comportment:
{% set testTrue = true %}
{% set testFalse = false %}
{% set testNull = null %}
{% set testString = "something" %}
{% if testTrue %} {# true #}
{% if testFalse %}{# false#}
{% if testNull %} {# false#}
{% if testString %} {# true #}
{% if not testTrue %}{# false#}
{% if not testFalse %}{# true#}
{% if not testNull %} {# false !!!!!!!!!!!!!!!! #}
{% if not testString %} {# false#}
{% if testTrue == true %} {# true #}
{% if testFalse == true %} {# false#}
{% if testNull == true %} {# false#}
{% if testString == true %} {# true #}
{% if not testTrue == true %} {# false#}
{% if not testFalse == true %} {# true #}
{% if not testNull == true %} {# true #}
{% if not testString == true %} {# false#}
other tests good to know
{% if testTrue == 1 %} {# true #}
{% if testFalse == 1 %} {# false#}
{% if testTrue == "1" %} {# true #}
{% if testFalse == "1" %} {# false#}

Accessing associative array in twig

I have this array:
$modules = array(
'users',
'submodule' => array(
'submodule1',
'submodule2',
),
);
My question is how can I access all the values and display it on html?
I have tried the following but no luck :
{% for key, module in modules %}
{% if modules.key is iterable %}
{{ module }}
{$ else %}
{{ module }}
{% endif %}
{% endfor %}
Thanks!
If your array has only 2 levels, you can just do something close to what you did:
{% for module in modules %}
{% if module is iterable %}
{% for submodule in module %}
<p>{{ submodule }}</p>
{% endfor %}
{% else %}
<p>{{ module }}</p>
{% endif %}
{% endfor %}
Will give you (with the context you given):
<p>users</p>
<p>submodule1</p>
<p>submodule2</p>
See fiddle
But if your array has an arbitrary number of levels, you should do some recursive using macros:
{% macro show_array(array) %}
{% from _self import show_array %}
{% for module in array %}
{% if module is iterable %}
{{ show_array(module) }}
{% else %}
<p>{{ module }}</p>
{% endif %}
{% endfor %}
{% endmacro %}
{% from _self import show_array %}
{{ show_array(modules) }}
With the following context (in YAML format):
modules:
0: users
submodule:
0: submodule1
1: submodule2
subsubmodule:
0: subsubmodule1
1: subsubmodule2
This will give you:
<p>users</p>
<p>submodule1</p>
<p>submodule2</p>
<p>subsubmodule1</p>
<p>subsubmodule2</p>
See fiddle

Resources