How can I break a for loop in jinja2? - python

How can I break out of a for loop in jinja2?
my code is like this:
<a href="#">
{% for page in pages if page.tags['foo'] == bar %}
{{page.title}}
{% break %}
{% endfor %}
</a>
I have more than one page that has this condition and I want to end the loop, once the condition has been met.

You can't use break, you'd filter instead. From the Jinja2 documentation on {% for %}:
Unlike in Python it’s not possible to break or continue in a loop. You can however filter the sequence during iteration which allows you to skip items. The following example skips all the users which are hidden:
{% for user in users if not user.hidden %}
<li>{{ user.username|e }}</li>
{% endfor %}
In your case, however, you appear to only need the first element; just filter and pick the first:
{{ (pages|selectattr('tags.foo', 'eq', bar)|first).title }}
This filters the list using the selectattr() filter, the result of which is passed to the first filter.
The selectattr() filter produces an iterator, so using first here will only iterate over the input up to the first matching element, and no further.

Break and Continue can be added to Jinja2 using the loop controls extension.
Jinja Loop Control
Just add the extension to the jinja environment.
jinja_env = Environment(extensions=['jinja2.ext.loopcontrols'])
as per sb32134 comment

But if you for some reason need a loop you can check the loop index inside for-loop block using "loop.first":
{% for dict in list_of_dict %}
{% for key, value in dict.items() if loop.first %}
<th>{{ key }}</th>
{% endfor %}
{% endfor %}

Related

start 'forloop.counter' from different index or add something to the result

basically i want to count the iteration and end few tags after every 4th block
and 1st block is put manually so therefore the forloop.counter wont get the work done either I need to make the count start from 4 or just add 2 to the result {% forloop.counter + 2%} but add like this throws error Unused '+2' at end of if expression I am new to Django how do I do this
To conditionally do something on every nth iteration of a for loop you can use the divisibleby template filter.
To start from the 2nd element you can use the add filter to add 2 to the loop counter and then chain this with the divisibleby filter
<div class="container">
{% for elm in elements %}
{% if forloop.counter0|add:"2"|divisibleby:"4" %}
</div>
<div class="container">
{% endif %}
<p>{{ elm }}</p>
{% endfor %}
</div>

Using for loop to seperate formsets with <hr> only between forms and not at beginning or end

I only want the horizontal rule between each form and not at the beginning or end.
How can I alter the code so that the last horizontal rule is removed and how many different ways are there to doing this?
<form method="POST">
{% csrf_token %}
{% for form in formset %}
{{ form.as_p }}
<hr>
{% endfor %}
<button type="submit">Add Family Members</button>
</form>
Here's another example:
mylist = ['Tom', 'John', 'Jack', 'Joe']
for i in mylist:
print(i)
print('----------')
How can I alter the code so that '--------' is printed between the names but not at the end?
You want to use forloop.counter, or
{% if not forloop.first and not forloop.last %}
<hr>
{% endfor %}
In a Django template the usual method is to test the forloop.first or forloop.last variables the template engine exposes during the loop.
EG
<form method="POST">
{% csrf_token %}
{% for form in formset %}
{{ form.as_p }}
{% if not forloop.last %}
<hr>
{% endif} %}
{% endfor %}
<button type="submit">Add Family Members</button>
</form>
In plain Python code I'd try to use join for the specific case of using a delimiter to combine elements of an iterable. In the general case I can't think of an alternative to maintaining state yourself, eg a boolean indicating whether this is the first iteration of the loop or using enumerate and testing the index. Or just executing once outside the loop if you're sure your iterable is non-empty and then looping.
join returns a single string, so it's suitable when you want to pass around the joined value as well as when you want to use it for output. To replicate your exact example you'd want to include a newline in your join string. Something like:
print('----------\n'.join(mylist))
But it's often useful outside prints as well.
An explicit loop would look something like:
first = true
for element in mylist:
if not first:
print('----------')
else:
first = false
print element
Or using enumerate:
for index, element in enumerate(mylist):
if index:
print('----------')
print element
That uses the fact that a numeric type evaluates as false when it equals 0 - if index != 0: or if index > 0: is logically equivalent here, since we know index will always be a non-None and non-negative integer.

iterating through list with floor loop jinja django python

I have this array:
scores = [45.62, 51.87, 33.12, 39.37, 33.12]
I want to iterate through the list, and pass each item to an html template.
Using jinga, i tried the following:
{% for items in scores %}
{‌{ items }}
<br>
{% endfor %}
I hoped that the above would print out each item in the list like so:
45.62
51.87
33.12
etc...
but it didn't, it just prints the entire list, as a list, on one line.
I also tried this:
{% for items in scores %}
{‌{ scores.0 }}
<br>
{% endfor %}
This printed out just the first score of the list, but not the others. I want to print out each score individually. Please help! I'm using django 1.9. I know this is jinja, not sure if it's jinja2?
Feels like list is not as you put in question.Try this
{% for items in scores.0 %}
{‌{ items }}
<br>
{% endfor %}
Try changing up the variable names. Maybe you have another variable called items in your context. It would make more sense to use a variable name that's not plural for the loop.
{% for score in scores %}
{‌{ score }}
<br>
{% endfor %}

Change first element style in django template loop divided in if else case

I have a loop in my djnago template which is divided in if else case and I need to change the first element style in only *if* case
<b><ul>
{% for i in prosize %}
{% if i.num_in_stock > 0 %}
<li ><a class="order" id="{{i.option1}}" href="javascript:setSize('{{i.option1}}')">{{i.option1}}</a></li>//**i need to chnage the first element style in this case**
{% else %}
<li><a style="background-color:#c2c2c2;color:#000;" href="#myModal" role="button" data-toggle="modal" >{{i.option1}}</a></li>
{% endif %}
{% endfor %}
</ul></b>
please suggest how can I do this ?
Use forloop.first to check whether current iteration is the first one. In case you wonder, yes, you can use boolean operators like and in if.
There are several methods provided by Django for "For loop". You can use below methods.
forloop.first - True if this is the first time through the loop
forloop.last - True if this is the last time through the loop
forloop.counter - The current iteration of the loop (1-indexed)
forloop.counter0 - The current iteration of the loop (0-indexed)
In your case the code should be like this :
<b><ul>
{% for i in prosize %}
{% if i.num_in_stock > 0 and forloop.counter == 1 %}
.....................

Django templates: accessing the previous and the following element of the list

I am rather new to django templates and have an impression that I have not understood some basics.
I have a list of elements and I need to render an element of the list based on conditions against the the previous and the next elements (in case the following or the previous elements are hidden, I need to mark the current element as border element).
How can I reference the previous and the following elements within a for loop in Django templates?
You could write a custom template filter for next and previous:
def next(value, arg):
try:
return value[int(arg)+1]
except:
return None
and in the template:
{% for ... %}
{% with list|next:forloop.counter0 as next %}
{% if next.is_hidden %}
...
{% endif %}
{% endwith %}
{% endfor %}
but like others have said, there are probably more elegants solutions doing this via your view
You can't do this strictly with built-in template tags. You need to involve some Python.
One method would be to zip the list with itself:
new = ([(None, orig[0], orig[1])] +
zip(orig, orig[1:], orig[2:]) +
[(orig[-2], orig[-1], None)])
and pass that to the template, then loop over it like this:
{% for prev, current, next in new %}
{% if prev.hidden or next.hidden %}
BORDER
{% endif %}
{{ current }}
{% endfor %}
You really shouldn't use the Django templates for this kind of logic. You have your views to handle it. I would figure out which elements of the list are borders and pass in an extra parameter that I can handle in the template using a simple if statement. For example:
{% for element,is_border in data.items %}
{% if is_border %}
do something
{% endif %}
{% endfor %}
There are tons of similar ways you can do this. I presented just one example.
You can create an external tag which does that but django templating system which was build to be lightweight has no such feature in for loops.

Categories

Resources