I want to loop over a list of objects and count how many objects meet a requirement. I based my code off other examples I'd found, but it doesn't work, the count is always 0 after the loop.
For each house, I want to loop over each room and count how many rooms have a bed. I want to output that then reset the count for the next house.
{% for house in city %}
{% set count = 0 %}
<div>{{ house.address }} has {{ count }} beds in it rooms.</div>
{% for room in house %}
{% if room.has_bed == True %}{% set count = count + 1 %}{% endif %}
{% endfor %}
{% endfor %}
Jinja 2.10 introduces the namespace object to handle assignment and comparison in loops.
{% set ns = namespace(beds=0) %}
{% for room in house %}
{% if room.has_bed %}
{% set ns.beds = ns.beds + 1 %}
{% endif %}
{% endfor %}
{{ house.address }} has {{ ns.beds }} beds.
Normally, set does not handle attributes, which is why the old answers mutate objects with methods instead. namespace has been special cased so setting attributes does work.
The reason the original counter didn't work is because of Jinja's scope rules. Unlike Python, most blocks are new scopes. set always defines a local variable, except in this new special case of namespace.attribute.
In this specific case, you can accomplish what you want with filters.
{% set beds = house.rooms|selectattr('has_bed')|length %}
{{ house.address }} has {{ beds }} beds.
However, there are cases where storing information across scopes is needed. For example, if you wanted to output some information in addition to incrementing the counter, it would make sense to use a namespace.
For Jinja 2.9, the scope behavior was fixed, invalidating code that worked in previous versions. The incremented value of count only lives within the scope of the loop. Their example involves setting variables, but the concept is the same:
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. The only exception to that rule are if statements which do not introduce a scope. As a result the following template is not going to do what you might expect:
{% set iterated = false %}
{% for item in seq %}
{{ item }}
{% set iterated = true %}
{% endfor %}
{% if not iterated %} did not iterate {% endif %}
It is not possible with Jinja syntax to do this.
You will need to do a hacky-ish workaround in order to track count across iterations. Set a list, append to it, then count its length.
{% for house in city %}
{% set room_count = [] %}
{% for room in house %}
{% if room.has_bed %}
{% if room_count.append(1) %}{% endif %}
{% endif %}
{% endfor %}
<div>{{ house.address }} has {{ room_count|length }} beds.</div>
{% endfor %}
For Jinja <= 2.8, the code you've shown does work. However, this was due to incorrect behavior in Jinja's scope rules, which was fixed in 2.9.
{% for house in city %}
{% set count = 0 %}
{% for room in house %}
{% if room.has_bed %}
{% set count = count + 1 %}
{% endif %}
{% endfor %}
<div>{{ house.address }} has {{ count }} beds.</div>
{% endfor %}
Related
I have a nested loop to display content and apply terms in my template.
{% for class in class %}
{% if saveclass %}
{% for saveclass in saveclass %}
{% if class.colecl in saveclass.saveclass_tag %}
save class exists!
{{ class.levelclass_name }}<br>
{% else %}
save class not exists!
{{ class.levelclass_name }}<br>
{% endif %}
{% endfor %}
{% else %}
there is nothing save class
{{ class.levelclass_name }}<br>
{% endif %}
{% endfor %}
The first loop gives me the list of classes.
Then it is checked whether the saveclass variable exists or not, if there is, the second loop is executed.
In the second loop it gives me saveclass and I check if the class tag is in the saveclass table and I display the output.
The problem is that the inner circle has to end to get to the next class. And for this reason, both the first if and its else are executed.
Python and other languages use breaks for this, but Django does not have a break template.
What is your solution?
Thank
I researched and according to friends Django template did not allow the break of the ring and should be managed in views.
I have a project in django, where I have to display clients in alphabetical order but names of companies can start from upper or lower case.
For now my solution below display names starts from lower case in one section and name starts from the same letter, but uppercase in another section.
{% regroup clients by title.0 as clients_list %}
{% for client in clients_list %}
{{ client.grouper }}
{% for item in client.list %}
{% if item.client_link %}
{{ item.title }}
{% else %}
{{ item.title }}
{% endif %}
{% endfor %}
{% endfor %}
How to display name starts from upper and lower case under one section?
Using Django built-in template filter called title
{{ item.title|title }} {% else %} {{ item.title|title }}
I'm trying to slice loop in django template with variable
USUAL WAY
{% for article in module.module_article_key.module_article_category.article_category_key.all|slice:":2" %}
{{ article.article_title }}
{% endfor %}
WHAT NEEDS
{% for article in module.module_article_key.module_article_category.article_category_key.all|slice:":module.module_article_key.module_article_count" %}
{{ article.article_title }}
{% endfor %}
so we have working variable {{ module.module_article_key.module_article_count }}
normaly this variable gives integer value stored for this module, however wen i use it to slice loop - nothing happens
You need to cast module_article_count to string first then making articleSlice via nested {% with %} and use the resulting template variable in slice filter as follow:
{% with articleCount=module.module_article_key.module_article_count|stringformat:"s" %}
{% with articleSlice=":"|add:articleCount %}
{% for article in module.module_article_key.module_article_category.article_category_key.all|slice:articleSlice %}
{{ article.article_title }}
{% endfor %}
{% endwith %}
{% endwith %}
in a jinja2 for loop, how can I keep track of what the previous value of a variable was (for purposes of displaying breaks between "groups")? The obvious and straight-forward answer of:
{% set last_val='unk' %}
{% for object in data %}
{% if object[0]!=last_val %}
<output whatever separation code>
{% set last_val=object[0] %}
{% endif %}
<other stuff>
{% endfor %}
...doesn't work due to jinja2's scoping rules - each new time through the loop sees the same 'unk'. How can I work around this limitation?
EDIT: I was poking around some older code of mine where I had done similar things, and apparently this DID work with jinja2 2.8, but broke sometime before 2.9.6. So I guess one solution would be to downgrade to 2.8 and just stay there.
Use "loop.previtem" .
{% for object in data %}
{% if loop.index0 ==0 %}
<output whatever separation code>
{% elif loop.index0 > 0 and loop.previtem[0] != object[0] %}
<output whatever separation code>
{% endif %}
<other stuff>
{% endfor %}
jinja is not able to change the value of variable ,but you can use dictionary to change value of key .
this will work .
{% set last_val={'key':'unk'} %}
{% for object in data %}
{% if object[0]!=last_val.key %}
<output whatever separation code>
{% set test = last_val.update({'unk':object[0]}) %}
{% endif %}
<other stuff>
{% endfor %}
I am working on a Django template, and I need to print my statement once during the loop iteration. I've tried {% ifchanged %}, but it's not working inside two loops.
Using {% ifchanged %} works under a single loop, but I am trying this inside two loops.
For example:
{% for i in j %}
{% for k in j %}
{% ifchanged %}
//something here//
{% endifchanged %}
{% endfor %}
{% endfor %}
However, in this case it's not working.
There’s also forloop.first.
See the Django Built-in template tags and filters documentation for the variables associated with forloops.
{% if forloop.first %}
// something here //
{% endif %}
There is also a forloop.last, if it needs to show up at the end, and a forloop.counter.