Jinja docx template, avoiding new line in nested for

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))

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


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


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.