Does Jinja2 support nested if statements? - python

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 %}

Related

get_children not working. get_descendants does. But i can't use that

I am currently working on the navbar of a project with Django-cms. I am fairly new to this framework and language, so sorry if this is a stupid question.
This has double dropdowns, which respond to user changes in the Django-cms admin interface.
Which works. Sort of.
The problem is that get_children doesn't work (no errors or something, just not detecting children, and showing the 'should be dropdown button' as a non dropdown version), but get_descendants does. But if i use that the content of the second dropdown will be shown again in the first dropdown. So get_children will be perfect, as it will only show the direct descendants, instead of all.
{% load cms_tags menu_tags sekizai_tags staticfiles%}
{% load menu_tags %}
{% for child in children %}
<!--non dropdown 1-->
{% if child.is_leaf_node %}
<li>{{child.get_menu_title }}</li>
{% endif %}
<!--dropdown 1-->
{% if not child.is_leaf_node or child.ancestor %}
<div class="dropdown">
<li>{{child.get_menu_title }}<b class="caret"></b></li>
<!-- dropdown 1 content-->
{% if child.get_descendants %}
<div class="dropdown-content">
{% for kid in child.get_descendants %}
<!--non dropdown 2-->
{% if kid.is_leaf_node %}
<li>{{kid.get_menu_title }}</li>
{% endif %}
<!--dropdown 2 -->
{% if not child.is_leaf_node or child.ancestor %}
<li>
<a class="menu-has-sub">{{kid.get_menu_title }}<i class="fa fa-angle-down"></i></a>
<!-- dropdown 2 content-->
<ul class="sub-dropdown droppeddown">
{% for baby in kid.get_descendants %}
<li>{{baby.get_menu_title }}</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
{% endif %}
{% endfor %}
So my question is: Why can't i use children
EDIT: *Why can't i use get_children. As in the function. No child labour here.
Nvm i fixed it! The syntax in this case should be children instead of get_children. which is funny because of that edit above.
Anyway, here's an example:
This doesn't work:
{% for kid in child.get_children %}
This does work:
{% for kid in child.children%}
hopefully this will help anyone else having this little struggle.

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 }}

Django if statement not working

{% extends "base.html" %}
{% block content %}
<h1>{{ page }}</h1>
{% for category in categories %}
{% if category.page == page %}
<h2>{{ category.title }}!</h2>
{% for item in categoryitems %}
{{ category.title }} {{ item.category }}
{% if item.category == category.title %}
<h3>{{ item.title }}</h3>
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endblock %}
The first statement if category.page == page is working fine, but the other one - if item.category == category.title - doesn't, for the sake of checking if everything is fine I've also added those {{category.title}} and {{item.category}} outputs and they are identical, so why doesn't the loop show me my item.title ? Without the if, it works, but, of course, shows every element in the list, which is smthn I don't need.
Do not depend on the page output to tell you what is correct, since the output is dependent on the code. Instead, do it correctly in the first place.
{% if item.category == category %}

How can I use break and continue in Django templates?

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.

Categories

Resources