How to Increment a Variable on a for Loop in Jinja Template

How to increment a variable on a for loop in jinja template?

You could use loop.index:

{% for i in p %}
{{ loop.index }}
{% endfor %}

Check the template designer documentation.

In more recent versions, due to scoping rules, the following would not work:

{% set count = 1 %}
{% for i in p %}
{{ count }}
{% set count = count + 1 %}
{% endfor %}

Jinja for loop scope is reset when incrementing variable

This is indeed a scoping issue, as documented in the Jinja2 template reference:

Scoping Behavior


Please keep in mind that it is not possible to set variables inside a block and have them show up outside of it. This also applies to loops.

[...]

As of version 2.10 more complex use cases can be handled using namespace objects which allow propagating of changes across scopes[.]

So you could use the namespace() class as a work-around:

{% set ns = namespace(items=0) %}
{% for line in current_order.order_lines %}
{% set ns.items = ns.items + line.quantity %}
{% endfor %}

{{ ns.items }}

That said, it is much better if you calculated the item count up front and passed this into the template as part of the current_order object or additional context.

Another option is to use the sum() filter to sum those quantities:

{% for line in current_order.order_lines %} #loops twice in current test
<!-- render order line -->
{% endfor %}

{{ current_order.order_lines|sum(attribute='quantity') }}

Updating Jinja Variable in loop

This sounds like a use case for WebSockets. The below is based on the example given in the documentation above, and can handle multiple connections, broadcasting the newly added comment to all the connected clients. Thus, if you open http://127.0.0.1:8000/ in multiple tabs in your browser, and add a new comment using one of these connections, every other will also receive the new comment. If you don't want to broadcast the message, then you could instead use await manager.send_personal_message(data, websocket).

app.py

from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect
from fastapi.templating import Jinja2Templates
import uvicorn

class ConnectionManager:
def __init__(self):
self.active_connections: List[WebSocket] = []

async def connect(self, websocket: WebSocket):
await websocket.accept()
self.active_connections.append(websocket)

def disconnect(self, websocket: WebSocket):
self.active_connections.remove(websocket)

async def send_personal_message(self, message: str, websocket: WebSocket):
await websocket.send_json(message)

async def broadcast(self, message: str):
for connection in self.active_connections:
await connection.send_json(message)

class Comment:
def __init__(self, author, content):
self.author = author
self.content = content

app = FastAPI()
templates = Jinja2Templates(directory="templates")
manager = ConnectionManager()
comments = []
comments.append( Comment("author 1 ", "content 1") )
comments.append( Comment("author 2 ", "content 2") )
comments.append( Comment("author 3 ", "content 3") )

@app.get("/")
def main(request: Request):
return templates.TemplateResponse("index.html", {"request": request, "comments": comments})

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await manager.connect(websocket)
try:
while True:
data = await websocket.receive_json()
comments.append(Comment(data['author'], data['content']))
await manager.broadcast(data)
except WebSocketDisconnect:
manager.disconnect(websocket)

if __name__ == '__main__':
uvicorn.run(app, host='127.0.0.1', port=8000)

templates/index.html

<!DOCTYPE html>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Add new comment</h1>
<form action="" onsubmit="addComment(event)">
<input type="text" id="author" autocomplete="off"/>
<input type="text" id="content" autocomplete="off"/>
<button>Add comment</button>
</form>
<h2>Comments</h2>
<ul id='comments'>
{% for comment in comments %}
<li>
<h3> {{comment.author}} </h3>
<p> {{comment.content}} </p>
</li>
{% endfor %}
</ul>
<script>
var ws = new WebSocket("ws://localhost:8000/ws");
ws.onmessage = function(event) {
var comments = document.getElementById('comments')
var comment = document.createElement('li')
var jsonObj = JSON.parse(event.data);
var authorNode = document.createElement('h3');
authorNode.innerHTML = jsonObj.author;
var contentNode = document.createElement('p');
contentNode.innerHTML = jsonObj.content;
comment.appendChild(authorNode);
comment.appendChild(contentNode);
comments.appendChild(comment)
};
function addComment(event) {
var author = document.getElementById("author")
var content = document.getElementById("content")
ws.send(JSON.stringify({"author": author.value, "content": content.value}))
author.value = ''
content.value = ''
event.preventDefault()
}
</script>
</body>
</html>

Jinja2: Change the value of a variable inside a loop

Try also dictionary-based approach. It seems to be less ugly.

{% set vars = {'foo': False} %}

{% for item in items %}
{% if vars.update({'foo': True}) %} {% endif %}
{% if vars.foo %} Ok(1)! {% endif %}
{% endfor %}

{% if vars.foo %} Ok(2)! {% endif %}

This also renders:

Ok(1)!
Ok(2)!

How can I make a simple counter with Jinja2 templates?

With variable inner group sizes, this will work:

from jinja2 import Template

items = [
['foo', 'bar'],
['bax', 'quux', 'ketchup', 'mustard'],
['bacon', 'eggs'],
]

print Template("""
{% set counter = 0 -%}
{% for group in items -%}
{% for item in group -%}
item={{ item }}, count={{ counter + loop.index0 }}
{% endfor -%}
{% set counter = counter + group|length %}
{% endfor -%}
""").render(items=items)

...which prints:

item=foo, count=0
item=bar, count=1

item=bax, count=2
item=quux, count=3
item=ketchup, count=4
item=mustard, count=5

item=bacon, count=6
item=eggs, count=7

I guess variables declared outside up more than one level of scope can't be assigned to or something.

Problem setting the Jinja variable during iteration in flask

Instead of assigning a separate variable, isn't this possible within the for loop itself? I'm assuming you want the loop to execute 4 times and the two consecutive images should be displayed in each iteration. This is what's done here by modifying the for loop to meet that criteria.

<div class = "methods">
{% for i in range (1, 9, 2) %}
<div class = "mrow">
<div class = "mrow1">
<div class = "mrow11">
<img src = "static/images/metodo{{ listofitems[i] }}.png">
</div>
<div class = "mrow2 gold">
MÉTODO
</div>
</div>
<div class = "mrow1">
<div class = "mrow11">
<img src = "static/images/metodo{{ listofitems[i+1] }}.png">
{{ i }}
</div>
<div class = "mrow2 gold">
MÉTODO
</div>
</div>
</div>
{% endfor %}
</div>

Increment a value for each loop

If you want to have several lines in the same template file, you should not loop in your task but in your template only.

In this case, you should refer to the Jinja2 for structure documentation and use the corresponding special variables available inside the loop.

Given the following file.j2 template

{% for item in my_var %}
configuration_{{ item }}:
value:
- {{ loop.index + offset | default(0) | int }}
path:
- /var/opt/{{ item }}
{% endfor %}

And the following play.yml playbook

---
- hosts: localhost
gather_facts: false

vars:
my_var:
- aconfig
- otherconfig
- lastconfig

offset: 100

tasks:

- name: show the template result as a debug output
debug:
msg: "{{ lookup('template', 'file.j2').split('\n') }}"

- name: actually write the output file on disk
template:
src: file.j2
dest: /tmp/trash_it.yaml

We get:

# Run the playbook with configured offset in vars
$ ansible-playbook play.yml

PLAY [localhost] **************************************************************************************************************************************************************************************************

TASK [show the template result as a debug output] *****************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
"configuration_aconfig:",
" value:",
" - 101",
" path:",
" - /var/opt/aconfig",
"configuration_otherconfig:",
" value:",
" - 102",
" path:",
" - /var/opt/otherconfig",
"configuration_lastconfig:",
" value:",
" - 103",
" path:",
" - /var/opt/lastconfig",
""
]
}

TASK [actually write the output file on disk] *********************************************************************************************************************************************************************
changed: [localhost]

PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

# Demo it works for any value overriding the offset in an extra var
$ ansible-playbook play.yml -e offset=583

PLAY [localhost] **************************************************************************************************************************************************************************************************

TASK [show the template result as a debug output] *****************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
"configuration_aconfig:",
" value:",
" - 584",
" path:",
" - /var/opt/aconfig",
"configuration_otherconfig:",
" value:",
" - 584",
" path:",
" - /var/opt/otherconfig",
"configuration_lastconfig:",
" value:",
" - 585",
" path:",
" - /var/opt/lastconfig",
""
]
}

TASK [actually write the output file on disk] *********************************************************************************************************************************************************************
changed: [localhost]

PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0


Related Topics



Leave a reply



Submit