How do I check for last loop iteration in Django template? - django

I have a basic question, in the Django template language how can you tell if you are at the last loop iteration in a for loop?

You would use forloop.last. For example:
<ul>
{% for item in menu_items %}
<li{% if forloop.last %} class='last'{% endif %}>{{ item }}</li>
{% endfor %}
</ul>

{{ forloop.last }}

You can basically use this logic in a for loop:
{% if forloop.last %}
# Do something here
{% endif %}
For example, if you need to put a comma after each item except for the last one, you can use this snippet:
{% for item in item_list %}
{% if forloop.last %}
{{ item }}
{% else %}
{{ item }},
{% endif %}
{% endfor %}
which will become for a list with three items:
first_item, second_item, third_item

Related

Django: Count forloop.first only if a second if condition is met

I have the following (oversimplified example):
{% for item in myitems %}
{% if item == "orange" %}
{{item}}
{% endif %}
{% endfor %}
Let's say that my list myitems is ['apple','orange','watermelon','orange']. The above code prints twice "orange". But I don't want this... I would like to print it once. So then I tried using if forloop.first
{% for item in myitems %}
{% if item == "orange" %}
{% if forloop.first %}
{{item}}
{% endif %}
{% endif %}
{% endfor %}
The first loop will be item=apple so it won't print the item because it is not "orange". In the second loop now we have item=orange but it no longer fulfills if forloop.first as it is the second loop already so it won't print the item. How can I print orange once? Is there a statement such us: if forloop.first (start numbering from the first print)
I think you can print the element of myitems only once when condition is met using a variable in the template and changing its value when the condition is met:
{% set stop_loop="" %}
{% for item in myitems %}
{% if stop_loop %}
{% elif item == "orange" %}
{{item}}
{% set stop_loop="true" %}
{% endif %}
{% endfor %}
IMO, this kind of business logics should be in the view rather than template.
If I've correctly understood, you're basically looking for this piece of code:
{% for item in myitems %}
{% if item == "orange" and forloop.first %}
{{item}}
{% endif %}
{% endfor %}
A simple and should do the job. With the example you provided ['apple','orange','watermelon'], the rendered template would be blank.
This is my solution based on previous comments
{% with False as stop_loop %}
{% for item in myitems %}
{% if stop_loop %}
{% elif item == "orange" %}
{{item}}
{% update_variable True as stop_loop %}
{% endif %}
{% endfor %}
{% endwith %}
And I need to register in templatetags the following:
#register.simple_tag
def update_variable(value):
return value
It works

How to render the first element of a list different than others in DTL

I want to render the first element of a list differently in Django Template Language. How can I differentiate the first iteration from others? Or is there any different way?
I have tried doing -
{% for item in results %}
{% if loop.first %}
First Element
{% else %}
Other element
{% endif %}
{% endfor %}
What am I doing wrong?
You should use forloop instead of loop
{% for item in results %}
{% if forloop.first %}
First Element
{% else %}
Other element
{% endif %}
{% endfor %}

Django loop – remove last comma

I have the following loop set up, but need to remove the comma on the last item (it's to replicate a JSON array for cycle2)
{% for product_in_series in series.get_products %}{%spaceless%}
{% with product_in_series.product as product %}
{%if not forloop.first%}
"<img src='{% version product.get_overview 'page_image' %}'>",
{%endif%}
{% endwith %}
{%endspaceless%}{% endfor %}
Cheers,
R
What about this?
{% for product_in_series in series.get_products %}{%spaceless%}
{% with product_in_series.product as product %}
{%if not forloop.first%}
"<img src='{% version product.get_overview 'page_image' %}'>"
{%if not forloop.last%},{%endif%}
{%endif%}
{% endwith %}
{%endspaceless%}{% endfor %}
{{ forloop.last|yesno:",,"|safe }}
, - is a comma
None of the above works for me.
The correct syntax, as in Django 3.0, is as such
{% with querythisandthat as A %}
{% for u in A %} {{ u.interesting_stuff }}
{% if u == A.last %} . {% else %} ; {% endif %}
{% endfor %}
{% endwith %}
The reason is that A.last is not True/False, but it is the last element of the queryset
https://docs.djangoproject.com/en/3.0/ref/models/querysets/#django.db.models.query.QuerySet.first

Django template: check for empty query set

Is there a way to check for an empty query set in the Django template? In the example below, I only want the NOTES header to be displayed if there are notes.
If I put an {% empty %} inside the "for" then it does display whatever is inside the empty tag, so it knows it's empty.
I'm hoping for something that does not involve running the query twice.
{% if notes - want something here that works %}
NOTES:
{% for note in notes %}
{{note.text}}
{% endfor %}
{% endif %}
Clarification: the above example "if notes" does not work - it still displays the header even with an empty query set.
Here's a simplified version of the view
sql = "select * from app_notes, app_trips where"
notes = trip_notes.objects.raw(sql,(user_id,))
return render_to_response(template, {"notes":notes},context_instance=RequestContext(request))
Edit: the view select selects from multiple tables.
Have a look at the {% empty %} tag.
Example from the documentation
<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% empty %}
<li>Sorry, no athletes in this list.</li>
{% endfor %}
</ul>
Link: https://docs.djangoproject.com/en/1.8/ref/templates/builtins/#for-empty
If you are interested in a table, or some kind of heading if there are results, add the forloop.first:
{% for athlete in athlete_list %}
{% if forloop.first %}
Athlete Name:
{% endif %}
{{ athlete.name }}
{% empty %}
Sorry, no athletes in this list.
{% endfor %}
Try {% if notes.all %}. It works for me.
In your view check whether notes is empty or not. If it is then you pass None instead:
{"notes": None}
In your template you use {% if notes %} as normal.
It's unfortunate that you're stuck using a raw query set - they're missing a lot of useful behavior.
You could convert the raw query set into a list in the view:
notes_as_list = list(notes)
return render_to_response(template, {"notes":notes_as_list},context_instance=RequestContext(request))
Then check it as a boolean in the template:
{% if notes %}
Header
{% for note in notes %}
{{ note.text }}
{% endfor %}
{% endif %}
You could also make it happen without conversions using forloop.first:
{% for note in notes %}
{% if forloop.first %}
Header
{% endif %}
{{ note.text }}
{% endfor %}
What about:
{% if notes != None %}
{% if notes %}
NOTES:
{% for note in notes %}
{{ note.text }}
{% endfor %}
{% endif %}
{% else %}
NO NOTES AT ALL
{% endif %}
Your original solution
{% if notes %}
Header
{% for note in notes %}
{{ note.text }}
{% endfor %}
{% endif %}
Works now with Django 1.7 and thanks to QuerySet caching, it does not cost and extra query.
Often the right way to do this is to use the {% with ... %} tag. This caches the query so it runs only once and also gives you more flexibility with your markup than using {% empty %}.
{% with notes as my_notes %}
{% if my_notes %}
<ul>
{% for note in my_notes %}
<li>{{ note }}</li>
{% endfor %}
</ul>
{% else %}
<p>Sorry, no notes available</p>
{% endif %}
{% endwith %}
With this particular example I'm not sure how useful it is but if you're querying Many-to-Many field, for instance, it's likely what you want to do.
Use {% empty %} in django templates
{% if list_data %}
{% for data in list_data %}
{{ data.field_1 }}
{% endfor %}
{% else %}
<p>No data found!</p>
{% endif %}
We can write above code with {% empty %}.
{% for data in list_data %}
{{ data.field_1 }}
{% empty %}
<p>No data found!</p>
{% endfor %}

How do you access the menu model from a Zotonic template?

I want to write my own style of menu, but I would prefer to do it in the templates rather than making my own menu scomp.
I basically want to be able to do something like:
{% if m.menu %}
<ul>
{% for top_level_id in m.menu %}
{% with m.rsc[top_level_id] as top_level %}
<li>{{ top_level.title }}
{% if top_level.menu %}
<ul>
{% for mid_level_id in top_level.menu %}
{% with m.rsc[mid_level_id] as mid_level %}
<li>{{ mid_level.title }}</li>
{% endwith %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endwith %}
{% endfor %}
</ul>
{% endif %}
How do you access the menu model from a Zotonic template?
To add to my previous answer. The standard _menu.tpl receives a list with all menu items. This list is the result of a depth-first tree walk of the complete menu. Every menu is a record with
{MenuRscId, DepthOfMenu, NrInSubMenu, HasSubMenuFlag}
Where the top level menu has a depth of 1 and the first menu item in a menu has a nr of 1.
All menu items that the current user is not allowed to see are filtered out.
The code of the default template:
<ul id="{{ id_prefix }}navigation" class="clearfix at-menu do_superfish">
{% for mid,depth,nr,has_sub in menu %}
{% if not mid %}{% if depth > 1 %}</ul></li>{% endif %}
{% else %}
{% if nr == 1 and not forloop.first %}<ul{% if mid|member:path %} class="onpath"{% endif %}>{% endif %}
<li id="{{ id_prefix }}nav-item-{{nr}}"
class="{% if is_first %}first {% endif %}{% if is_last %}last{% endif %}">
<a href="{{ m.rsc[mid].page_url }}"
class="{{ m.rsc[mid].name }}{% if mid == id %} current{% else %}{% if mid|member:path %} onpath{% endif %}{% endif %}">{{ m.rsc[mid].short_title|default:m.rsc[mid].title }}</a>
{% if not has_sub %}</li>{% endif %}
{% endif %}
{% endfor %}
{% if forloop.last %}{% include "_menu_extra.tpl" %}{% endif %}
</ul>
The (upcoming) 0.5-release and tip of Zotonic use a template to display the menu. Check mod_menu/templates/_menu.tpl.
This template is called by the menu scomp.