How can I use break and continue in Django templates? - python

I want to put break and continue in my code, but it doesn't work in Django template. How can I use continue and break using Django template for loop. Here is an example:
{% for i in i_range %}
{% for frequency in patient_meds.frequency %}
{% ifequal frequency i %}
<td class="nopad"><input type="checkbox" name="frequency-1" value="{{ i }}" checked/> {{ i }} AM</td>
{{ forloop.parentloop|continue }} ////// It doesn't work
{ continue } ////// It also doesn't work
{% endifequal %}
{% endfor%}
<td class="nopad"><input type="checkbox" name="frequency-1" value="{{ i }}"/> {{ i }} AM</td>
{% endfor %}

Django doesn't support it naturally.
You can implement forloop|continue and forloop|break with custom filters.
http://djangosnippets.org/snippets/2093/

For-loops in Django templates are different from plain Python for-loops, so continue and break will not work in them. See for yourself in the Django docs, there are no break or continue template tags. Given the overall position of Keep-It-Simple-Stupid in Django template syntax, you will probably have to find another way to accomplish what you need.

For most of cases there is no need for custom templatetags, it's easy:
continue:
{% for each in iterable %}
{% if conditions_for_continue %}
<!-- continue -->
{% else %}
... code ..
{% endif %}
{% endfor %}
break use the same idea, but with the wider scope:
{% set stop_loop="" %}
{% for each in iterable %}
{% if stop_loop %}{% else %}
... code ..
under some condition {% set stop_loop="true" %}
... code ..
{% endif %}
{% endfor %}
if you accept iterating more than needed.

If you want a continue/break after certain conditions, I use the following Simple Tag as follows with "Vanilla" Django 3.2.5:
#register.simple_tag
def define(val=None):
return val
Then you can use it as any variable in the template
{% define True as continue %}
{% for u in queryset %}
{% if continue %}
{% if u.status.description == 'Passed' %}
<td>Passed</td>
{% define False as continue %}
{% endif %}
{% endif %}
{% endfor %}
Extremely useful for any type of variable you want to re-use on template without using with statements.

Related

Django Template For Loop - reverese last two iterations

I am using Django 1.10 and Python 3.5.
I have been given some code that consists of a for loop that returns 3 iterations of value in alphabetical order.
For example the for loop returns: Chronological, Combination, Functional.
However I need the for loop to return: Chronological, Functional, Combination (the last two iterations/values are reversed). I am forced to keep the for loop.
Is this at all possible with a for loop?
I have tried combining {% if forloop.last %} with {% if forloop.first%} and also setting the value with {% if forloop.counter == 2 %}...set value to Functional...{% endif %} and {% if forloop.counter == 3 %}...set value to Combination...{% endif %} but I cannot get this to achieve what I want.
Here is my code:
{% for resume_format_image in resume_format_images %}
<div class="col-md-4">
{% with "resumes/resume_format_description_"|add:resume_format_image.style.lower|add:".html" as template_name %}
{% include template_name %}
{% endwith %}
</div>
{% endfor %}
I just figured this out:
{% for resume_format_image in resume_format_images %}
<div class="col-md-4">
{% if forloop.first%}
{% include "resumes/resume_format_description_chronological.html" %}
{% elif forloop.last%}
{% include "resumes/resume_format_description_combination.html" %}
{% else %}
{% include "resumes/resume_format_description_functional.html" %}
{% endif %}
</div>
{% endfor %}
I hope this helps someone.

Django {% blocktrans %}: How to handle pluralization inside a for loop?

I have the following loop in my Django template:
{% for item in state.list %}
<div> HTML (CUSTOMERS BY STATE) </div>
<!-- print sum of customers at bottom of list -->
{% if forloop.last %}
<h4>{{ forloop.counter }} Valued Customers</h4>
{% endif %}
{% endfor %}
Obviously, if I end up with only one customer, I'd like to print singular "Valued Customer"
According to Django's docs, one should use blocktrans. Tried the following, a few flavors of nesting:
{% blocktrans count %}
{% if forloop.last %}
<h4>
{{ forloop.counter }}
Valued Customer
{% plural %}
Valued Customers
</h4>
{% endif %}
{% endblocktrans %}
Keep getting TemplateSyntaxError: Invalid block tag: 'blocktrans', expected 'empty' or 'endfor'
Is there no way to combine with another loop? Any ideas how to solve? Thanks!
Here is the working code, thanks to alko:
{% load i18n %}
<!-- ... -->
{% if forloop.last %}
<h4>
{{ forloop.counter }}
{% blocktrans count count=forloop.counter %}
Valued Customer
{% plural %}
Valued Customers
{% endblocktrans %}
</h4>
{% endif %}
Probably, you forgot to load translation tags. Add following line at the top of your template:
{% load i18n %}
After you fix that, note that for a blocktrans tag after count a variable, whose value will serve for plural detection, should be specified, so you probably need something like
{% blocktrans count count=forloop.counter %}
To pluralize use this:
Customer{{ forloop.counter|pluralize }}

Does Jinja2 support nested if statements?

I read the docs and I am not clear on this is right at all. I know you can use nested for loops, but if statements seem to be different.
Can i do the following?
{% if thing=true %}
<div> something here</div>
{% if diffthing=true %}
<div> something else</div>
{% else %}
<div> third thing</div>
{% endif %}
{% else %}
<div> nothing here </div>
{% endif %}
Or should the format be different somehow?
Jinja2 supports nested blocks, including if statements and other control structures.
See the documentation on Block Nesting and Scope: "Blocks can be nested for more complex layouts."
A good use case for this is writing macros that conditionally output HTML:
{# A macro that generates a list of errors coming back from wtforms's validate function #}
{% macro form_error_summary(form, li_class='bg-danger') %}
{# only do the following on error... #}
{% if form.errors %}
<ul class="errors">
{# you can do layers of nesting as needed to render your content #}
{% for _field in form %}
{% if _field.errors %}
{% for error in _field.errors %}
<li class={{li_class}}>{{_field.label}}: {{ error|e }}</li>
{% endfor %}
{% endif %}
{% endfor %}
</ul>
{% endif %}
{% endmacro %}
The answer is yes.
I'm using logic very similar to yours in a live application and the nested if blocks work as expected. It can get a little confusing if you don't keep your code clean, but it works fine.
It seem possible. Refer to the documentation here: http://jinja.pocoo.org/docs/templates/#if
Just a quick add, if you're unpacking data to populate your fields, Jinja will only unpack it once. I had a similar problem with MongoDB and found if you change the item to a list item you iterate through it more than once without nesting
#app.route("/")
#app.route("/get_shrink")
def get_shrink():
# find and sort shrink top 5
shrink = list(mongo.db.shrinkDB.find().limit(5).sort(
"amount_lost_value", -1,))
return render_template(
"shrink.html", shrinkDB=shrink)
{% for shrink in shrinkDB %}
{% if shrink.resolved == true %}
<li>{{ shrink.product_name }} ||£ {{ shrink.amount_lost_value }} || {{ shrink.date }}</li>
{% endif %}
{% endfor %}
</span>
</div>
</div>
<div class="col s12 m5 offset-m2">
<h4>Top 5 Resolved Threats</h4>
<div class="card-panel light-blue">
<span class="white-text">
<!-- Shrink For loop top 5 resolves-->
{% for shrink in shrinkDB %}
{% if shrink.resolved != true %}
<li>{{ shrink.product_name }} ||£ {{shrink.amount_lost_value }} || {{ shrink.date }}</li>
{% endif %}
{% endfor %}

How can I concatenate forloop.counter to a string in my django template

I am already trying to concatenate like this:
{% for choice in choice_dict %}
{% if choice =='2' %}
{% with "mod"|add:forloop.counter|add:".html" as template %}
{% include template %}
{% endwith %}
{% endif %}
{% endfor %}
but for some reason I am only getting "mod.html" and not the forloop.counter number. Does anyone have any idea what is going on and what I can do to fix this issue? Thanks alot!
Your problem is that the forloop.counter is an integer and you are using the add template filter which will behave properly if you pass it all strings or all integers, but not a mix.
One way to work around this is:
{% for x in some_list %}
{% with y=forloop.counter|stringformat:"s" %}
{% with template="mod"|add:y|add:".html" %}
<p>{{ template }}</p>
{% endwith %}
{% endwith %}
{% endfor %}
which results in:
<p>mod1.html</p>
<p>mod2.html</p>
<p>mod3.html</p>
<p>mod4.html</p>
<p>mod5.html</p>
<p>mod6.html</p>
...
The second with tag is required because stringformat tag is implemented with an automatically prepended %. To get around this you can create a custom filter. I use something similar to this:
http://djangosnippets.org/snippets/393/
save the snipped as some_app/templatetags/some_name.py
from django import template
register = template.Library()
def format(value, arg):
"""
Alters default filter "stringformat" to not add the % at the front,
so the variable can be placed anywhere in the string.
"""
try:
if value:
return (unicode(arg)) % value
else:
return u''
except (ValueError, TypeError):
return u''
register.filter('format', format)
in template:
{% load some_name.py %}
{% for x in some_list %}
{% with template=forloop.counter|format:"mod%s.html" %}
<p>{{ template }}</p>
{% endwith %}
{% endfor %}
You probably don't want to do this in your templates, this seems more like a views job: (use of if within a for loop).
chosen_templates=[]
for choice in choice_dict:
if choice =='2':
{% with "mod"|add:forloop.counter|add:".html" as template %}
template_name = "mod%i.html" %index
chosen_templates.append(template_name)
Then pass chosen_templates to your template where you will have only
{% for template in chosen_templates %}
{% load template %}
{% endfor %}
Also, I don't quite understand why you are using a dict to select the template with a number that is not in the dictionnary. for key,value in dict.items() may be what you are looking for.
Try without using the block "with"
{% for choice in choice_dict %}
{% if choice =='2' %}
{% include "mod"|add:forloop.counter|add:".html" %}
{% endif %}
{% endfor %}

In a Django template for loop, checking if current item different from previous item

I'm new to django and can't find a way to get this to work in django templates. The idea is to check if previous items first letter is equal with current ones, like so:
{% for item in items %}
{% ifequal item.name[0] previous_item.name[0] %}
{{ item.name[0] }}
{% endifequal %}
{{ item.name }}<br />
{% endforeach %}
Maybe i'm trying to do this in wrong way and somebody can point me in right direction.
Use the {% ifchanged %} tag.
{% for item in items %}
{% ifchanged item.name.0 %}
{{ item.name.0 }}
{% endifchanged %}
{% endfor %}
Also remember you have to always use dot syntax - brackets are not valid template syntax.

Categories

Resources