Jinja docx template, avoiding new line in nested for
Asked Answered
T

5

17

I was wondering if there is a way to avoid newline characters inside a for in a docx template with jinja. The code is as follows:

{% for customer in customers %}
{% for account in customer.accounts %}
{{ account.number }}
{% endfor %}{% endfor %}. 

But the result is account numbers separated by row:

234090

29292

29292

I'm testing on LibreOffice.

Could someone help me?

Thomsen answered 16/8, 2017 at 16:48 Comment(0)
S
27

understanding where the extra linebreaks (and thus lines) come from

Whitespace in a Jinja template isn't being ignored. So what Jinja sees is

{% for customer in customers %}¶
{% for account in customer.accounts %}¶
{{ account.number }}¶
{% endfor %}{% endfor %}.·¶

And it actually doesn't care about lines too much, so make that

{% for customer in customers %}¶{% for account in customer.accounts %}¶{{ account.number }}¶{% endfor %}{% endfor %}.·¶

So that is the loop

{% for customer in customers %}…{% endfor %}.·

with body

¶{% for account in customer.accounts %}¶{{ account.number }}¶{% endfor %}

Note the at the beginning. The rest of the outer loop's body is another loop

{% for account in customer.accounts %}…{% endfor %}

with body

¶{{ account.number }}¶

Note the s at the beginning and end.

So you'll get a line break before the group accounts of each separate customer, and another line break before and after each account number. You probably don't want to get rid of all of them, because that would glue all the numbers together on a single line without any separation:

2340902929229292

mitigation

You can just avoid the line breaks except for the ones you want:

{% for customer in customers %}{% for account in customer.accounts %}{{ account.number }}¶
{% endfor %}{% endfor %}.·¶

That makes the template hard to read, though. You can let Jinja2 ignore whitespace between template tags. To do that, add a - at the end of the tag preceding the whitespace in question or at the beginning of the tag following that whitespace (or both):

{% for customer in customers -%}
{% for account in customer.accounts -%}
{{ account.number }}
{% endfor -%}
{% endfor %}. 

or

{% for customer in customers %}
{%- for account in customer.accounts %}
{{- account.number }}
{%- endfor %}
{% endfor %}. 

or

{% for customer in customers -%}
{%- for account in customer.accounts -%}
{{- account.number }}
{% endfor -%}
{%- endfor %}. 

(See Jinja2 documentation)

This even allows you to use indentation without having that additional whitespace end up in the result:

{% for customer in customers -%}
  {% for account in customer.accounts -%}
    {{ account.number }}{{ '\n' -}}
  {% endfor -%}
{% endfor %}. 

or

{% for customer in customers %}
  {%- for account in customer.accounts %}
    {{- account.number }}{{ '\n' }}
  {%- endfor %}
{% endfor %}. 

or

{% for customer in customers -%}
  {%- for account in customer.accounts -%}
    {{- account.number }}{{ '\n' -}}
  {% endfor -%}
{%- endfor %}. 

I've used the fact that not just variables but also literals can be used in template tags, so that I can produce a line break with {{ '\n' }}. This is necessary with this style, as a - to eat the indentation would swallow the (literal literal) line break in your template source, too.

Sniff answered 16/8, 2017 at 17:29 Comment(2)
What means . at the end of each file? Shouldn't it print literally a .Nikianikita
The . at the end is taken from the code in the question. And yes, that does print literally . , if I remember correctly. Whether that was intended by the OP, I don't know.Sniff
H
6

You can suppress rendering of the below lines:

<% for ... %>
<% endfor %>
<% if ... %>
<% endif %>

by setting trim_blocks=True and lstrip_blocks=True in your jinja2 environment. See the example below, info from their docs

context = {'querystring': querystring, 'path': path, 'content': content}    
loader = jinja2.FileSystemLoader('templates/')
jinja_env = jinja2.Environment(loader=loader, trim_blocks=True, lstrip_blocks=True)
print(jinja_env.get_template('my_template.yaml').render(context))
Hessler answered 10/4, 2018 at 21:53 Comment(0)
R
4

Thanks for @das-g's detailed comments. He explained how the space comes but didn't clarify how Jinja eats the white space. I would like to add some comments here for me and for other people having the same issue.

{%- for customer in customers %}: remove white space before for-loop block

{% for customer in customers -%}: remove white space before each for-loop item

{%- endfor %}: remove white space after each for-loop item

{% endfor -%}: remove white space after for-loop block

In a simple word,

{% for %} has a line break after this tag, that is {% for %}¶

{% for -%} first remove the line-break after the tag then expand the loop

same idea for the endfor tag

Rouault answered 16/3, 2020 at 15:25 Comment(0)
E
3

Inspired by das-g answer, I had a similar issue, after a lot of trial and error I gave up and decided to always remove the newlines but add newline break by hand like he did, but only if not last index of loop, so my macro ended up like this:

{% macro service_envs() %}
{% if app_envs is defined %}
    environment:
{% for env in app_envs %}
      - {{ env }}{% if not loop.last %}{{ '\n' }}{% endif %}
{%- endfor %}
{% endif %}
{% endmacro %}

The result is:

    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    ports:
      - 5432:5432

The important part is removing spaces in the {%- endfor %} but adding it in the if statement. So in my case the spaces were preserved, but I had no newline for last item of list. So my next line would be just after my environment list. Now I don't need to add {{- }} every time i call the macro. Tested with ansible version 2.5.0

Eugenol answered 26/4, 2018 at 12:20 Comment(0)
E
0

If a new line is not required then we can put the statements in single line as <% for … %><% if … %><% endif %><% endfor %>.

With this Jinj2, parsing will not introduce a new line.

Epicurean answered 27/10, 2020 at 20:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.