14

I have list in Jinja2 that contain dicts in itself. Something like

items = [{'name':'name1', 'points':5}, {'name':'name2', 'points':7}, 
 {'name':'name3', 'points':2}, {'name':'name4', 'points':11}]

What I need is to get sum of all points and to print it somewhere later.

Currently what I got is:

{% set points = 0 -%}
{% for single_item in items -%}
    {% set points = points + single_item["points"] -%}
    {{points}}
{% endfor %}
{{ points }}

Result is: 5 12 14 25 0

Is there any way that I can get that points outside of loop has value 25 (last value from the loop)?

Perun
  • 217
  • 1
  • 2
  • 7

4 Answers4

47

Jinja2 includes a sum filter which will do this for you:

{{ items | sum(attribute='points') }}

See documentation here: https://jinja.palletsprojects.com/templates/#jinja-filters.sum

HackDolphin
  • 166
  • 11
Bartlett
  • 844
  • 6
  • 9
3

That sort of logic should usually go in the controller, not the template (separating logic from presentation). Preprocess your data accordingly, and pass items as well as total to the template:

from jinja2 import Template

template = Template(open('index.html').read())

items = [{'name': 'name1', 'points': 5},
         {'name': 'name2', 'points': 7},
         {'name': 'name3', 'points': 2},
         {'name': 'name4', 'points': 11}]

total = sum([i['points'] for i in items])

print template.render(items=items, total=total)

index.html:

<table>

{% for item in items %}
  <tr>
    <td>{{ item.name }}</td>
    <td>{{ item.points }}</td>
  </tr>
{% endfor %}

</table>

<strong>Total:</strong>{{ total }}

For details on the expression sum([i['points'] for i in items]) see list comprehensions.

Lukas Graf
  • 30,317
  • 8
  • 77
  • 92
  • 1
    Although this is good answer, and I know it can be done this way, for my case it won't work well. Will have several dictionaries with supporting values, and have total mess of code. I think I've found a way how to do this, need to test it bit more, and if it is working, will post answer. – Perun Aug 27 '14 at 11:16
  • Then you probably need to refactor your code. Can you edit your question to include an example of your code and several dictionaries? – Lukas Graf Aug 27 '14 at 11:20
3

I've managed to make it work, although solution is not elegant, but it is working:

{% set points = [0] -%}
{% for single_item in items -%}
    {% if points.append(points.pop()+ single_item["points"]) -%}{% endif %}
{% endfor %}
{{ points }}

points will be array with just one element that has sum.

It can be also done with included do extension, and that would replace {% if %} line.

Perun
  • 217
  • 1
  • 2
  • 7
-1

Found on: https://linuxtut.com/en/129012d5539a8724556d/

Solution 2: Use namespace <2019/12/21: From na90ya> It seems that variables are preserved when using namespace objects introduced in 2.10 of jinja2. (Reference material [1])

count_test3.j2

{%- set ns = namespace(cnt=1) -%}
{%- for i in range(3) -%}
{%- set ns.cnt = ns.cnt + 1 -%}
{{ ns.cnt }}
{% endfor -%}
result : {{ ns.cnt }}

But i didn't try it yet...

Niels
  • 537
  • 5
  • 22