How to run for loops in a Django template? - django

I want to run the for loop to show all images but the problem is that I have to change the "id" of each image (pic-1 -> pic-2 ...so on) I know how to do it in normal Python but I'm lost on how to do this in template of Django.
<div class="preview-pic tab-content">
<div class="tab-pane active" id="pic-1"><img src="{{product.image.url}}"></div>
{% for image in image_list %}
<div class="tab-pane" id="pic-2"><img src="http://placekitten.com/400/252" /></div>
<div class="tab-pane" id="pic-3"><img src="http://placekitten.com/400/252" /></div>
<div class="tab-pane" id="pic-4"><img src="http://placekitten.com/400/252" /></div>
<div class="tab-pane" id="pic-5"><img src="http://placekitten.com/400/252" /></div>
{% endfor %}
</div>
<ul class="preview-thumbnail nav nav-tabs">
<li class="active"><a data-target="#pic-1" data-toggle="tab"><img src="http://placekitten.com/200/126" /></a></li>
{% for image in image_list %}
<li><a data-target="#pic-2" data-toggle="tab"><img src="http://placekitten.com/200/126" /></a></li>
<li><a data-target="#pic-3" data-toggle="tab"><img src="http://placekitten.com/200/126" /></a></li>
<li><a data-target="#pic-4" data-toggle="tab"><img src="http://placekitten.com/200/126" /></a></li>
<li><a data-target="#pic-5" data-toggle="tab"><img src="http://placekitten.com/200/126" /></a></li>
{% endfor %}
</ul>
</div>

You can use {{ forloop.counter }} or {{ forloop.counter0 }}
{% for image in image_list %}
<div class="tab-pane" id="pic-{{ forloop.counter }}"><img src="http://placekitten.com/400/252" /></div>
{% endfor %}
Note that
{{ forloop.counter }} starting index 1
{{ forloop.counter0 }} starting index 0

You can use the forloop.counter to get the loop index:
{% for image in image_list %}
<div class="tab-pane" id="pic-{{ forloop.counter }}"><img src={{ image.url }} />
{% endfor %}
If you have your pic counter starting at 2, you can use the add template tag:
{% for image in image_list %}
<div class="tab-pane" id="pic-={{ forloop.counter|add:1 }}"><img src={{ image.url }} />
{% endfor %}

You can use the {{forloop.counter}} variable. This will return the index of the loop.
forloop.counter The current iteration of the loop (1-indexed)
forloop.counter0 The current iteration of the loop (0-indexed)
forloop.revcounter The number of iterations from the end of the loop (1-indexed)
forloop.revcounter0 The number of iterations from the end of the loop (0-indexed)
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.parentloop For nested loops, this is the loop surrounding the current one
For more information on built-in templates tags and filters : https://docs.djangoproject.com/en/2.2/ref/templates/builtins/

Related

Django templates avoid loop

I am working on a project and I have a slight confusion.
The Django Template index.html has following code:
<div class="carousel-item active">
{% for i in products|slice:"0:"%}
<div class="col-xs-3 col-sm-3 col-md-3">
<div class="card" style="width: 17rem;">
<div class="card-body">
{% for img in i.images.all %}
{% if forloop.counter == 1 %}
<img src={{img.img_url}} class="card-img-top" alt="...">
{% endif %}
{% endfor %}
<h6 class="card-title">{{i}}</h6>
{% for skus in i.skus.all %}
{% if forloop.counter == 1 %}
<h6 class="card-price">{{skus.price}} {{skus.currency}}</h6>
{% endif %}
{% endfor %}
Add to Cart
</div>
</div>
</div>
{% endfor %}
</div>
In this code, is there a way to eliminate the {% for skus in i.skus.all %}?
The all tag is getting all objects, but I am restricting the loop to run only one time through the if condition so that I can only get the first item.
Is there a way to eliminate the loops that have .all in them and restrict the statement to run only one time though any other way?
You can achieve by using either with tag to set a variable or direclty us as:
{% with skus=i.skus.first %}
<h6 class="card-price">{{skus.price}} {{skus.currency}}</h6
{% endwith %}
or
<h6 class="card-price">{{i.skus.first.price}} {{i.skus.first.currency}}</h6
You may be looking for the first queryset method:
<div class="card-body">
<img src={{i.images.first().img_url}} class="card-img-top" alt="...">
</div>

How can I reset a forloop.counter0 in django?

I have a code in django that creates a carousel for each card item. As I am looping through each image for each specific card, I realized the forloop.counter will keep continuing until the initial for loop has finished. So in my example, i is my initial loop and p is my secondary loop. i loops through cards and p loops through images within the carousel of each card. I need to reset the counter after one i so that the carousel counter starts from the beginning so that first image of every carousel is active.
I'd really appreciate any help
{% for i in inspections %}
<div class="card - mb-3" style="width: 40 rem;">
<div id="myCarousel" class="carousel slide" data-bs-ride="carousel">
<div class="carousel-indicators">
{% for p in photos.all %}
{% if p.inspection_id == i.id %}
<button type="button" data-bs-target="#myCarousel"
data-bs-slide-to="{{ forloop.counter0 }}"
class="{% if forloop.counter0 == 0 %} active {% endif %}"
aria-current="true"
aria-label="Slide {{forloop.counter0}}"></button>
{% endif %}
{% endfor %}
</div>
<div class="carousel-inner">
{% for p in photos.all %}
{% if p.inspection_id == i.id %}
<div class="carousel-item {% if forloop.counter0 == 0 %} active {% endif %}">
<img src="{{ p.InspImages.url }}"
class="img-responsive mx-auto d-block w-80" height="300"
alt="...">
</div>
{% endif %}
{% endfor %}
</div>
<button class="carousel-control-prev" type="button"
data-bs-target="#myCarousel" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="True"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button"
data-bs-target="#myCarousel" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="True"></span>
<span class="visually-hidden">Next</span>
</button>
</div>
<div class="card-body">
# body codes #
{% endfor %}

How do I display the number of object returned from a query

I want to display the number of objects returned from query search. I have tried
{{ p.count }} and {{ Post.count }}
Here is my post_list.html. I have read other posts that use those methods and they do not work for me. I know I am missing something.
{% extends 'posts/base.html' %}
{% block content %}
<div class="col-sm-6 col-sm-offset-3">
<h1>{{ title }}</h1>
<form method="get" action=" ">
<input type="text" name="q" placeholder="search" value="{{ request.GET.q }}"/>
<input type="submit" value=" search"/>
</form>
create
{% for p in queryset %}
<div class="row">
<div class="col-sm-12 "> <!-- i like col-sm-6 -->
<div class="thumbnail">
{% if p.image %}
<img src='{{ p.image.url }}' class="img-responsive" />
{% endif %}
<!--<img src="..." alt="...">-->
<div class="caption">
{% if p.draft %} <h3>Staff Only: Draft</h3> {% if p.publish > today %}<h3>Staff Only: Future Post</h3> {% endif %}
{% endif %}
<h3><a href='{{ p.get_absolute_url}}'>{{p.title}}</a> <small>{{p.publish | timesince }} </small> </h3>
{% if p.user.get_full_name %}<p>Author: {{ p.user.get_full_name }}</p>{% endif %}
<p>{{p.content | truncatechars:30}}</p>
<p>View
{% if user.is_authenticated %}
edit
delete
{% endif %}
</p>
</div>
</div>
</div>
<hr>
</div>
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if queryset.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ queryset.number }} of {{ queryset.paginator.num_pages }}.
</span>
{% if queryset.has_next %}
next
{% endif %}
</span>
</div>
</div>
{% endblock content %}
Any help will be greatly appreciated. Thanks
You cannot do count on a single object p, because there's no count for that. Only queryset has count method, so do:
{{ queryset.count }}
You'll want to use the the built-in filter length
Returns the length of the value. This works for both strings and lists.
You can use it within your template to get the length of any object
{{ queryset|length }}

Dynamic carousel with django and bootstrap

I am working with a carousel to show some top rated products, the carousel works but I cant make work properly it seems that the problem is with the 'active' class . Each 'slide' shows 4 products.
This is my code
<div id="carousel-example" class="carousel slide hidden-xs" data-ride="carousel">
<div class="carousel-inner">
{% for p in prod %}
<div class="item {% if forloop.first %} active {% endif %}"> // here is the problem
<div class="row">
<div class="col-sm-3">
<h1>{{p.name}}</h1>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
If I don't use the forloop.first, the carousel doesn't slide. And with this forloop.first, it shows only one item per slide, instead of 4 items.
The output in the inspector is :
<div class="carousel-inner">
<div class="item active">
<div class="row">
<div class="col-sm-3"> // Here I expect 4 columns and I get only 1
</div>
</div>
</div>
<div class="item">
<div class="row">
<div class="col-sm-3">
</div>
</div>
</div>
</div>
Your issue is that you're trying to make a slide per each product: for each iteration of your loop you're creating a new item and put one col-sm-3 into it.
You can change your view to pass a nested structure to template or try to do something like:
<div id="carousel-example" class="carousel slide hidden-xs" data-ride="carousel">
<div class="carousel-inner">
{% for p in prod %}
{% cycle 'yes' '' '' '' as slidestart silent %}
{% if slidestart %} <div class="item {% if forloop.first %} active {% endif %}"> <div class="row">{% endif %}
<div class="col-sm-3">
<h1>{{p.name}}</h1>
</div>
{% if slidestart %} </div></div>{% endif %}
{% endfor %}
</div>
</div>
or use forloop.counter to put your items and rows each 4th row like that:
<div id="carousel-example" class="carousel slide hidden-xs" data-ride="carousel">
<div class="carousel-inner">
{% for p in prod %}
{% if forloop.counter0|divisibleby:"4" %}<div class="item {% if forloop.first %} active {% endif %}">
<div class="row">{% endif %}
<div class="col-sm-3">
<h1>{{p.name}}</h1>
{% if forloop.counter0|divisibleby:"4" %}</div>
</div>
</div>{% endif %}
{% endfor %}
</div>
</div>
The cycle answer above was closing the item everytime. Needed an 'end' cycle. This worked for me.. Thx x3al!
<div class="carousel-inner">
{% for track in track_set.all %}
{% cycle 'start' '' '' '' '' '' as slidestart silent %}
{% cycle '' '' '' '' '' 'end' as slideend silent %}
{% if slidestart %} <div class="item {% if forloop.first %} active {% endif %}"> <div class="row">{% endif %}
<div class="col-md-2">
.. slide code ..
</div>
{% if slideend %} </div></div>{% endif %}
{% endfor %}
</div>
Try this
<div {% if forloop.first %} class="item active" {% else %} class="item" {% endif %}>
I wanted to use this code to make 3 columned carousel, but the problem was when the object_list had number of items that are not dividable by the number of columns.
For example: it works with 6 columns because 6 % 3 = 0 but it doesn't work with 5 columns because 5 % 3 != 0
The problem was the cycle never got to the slidend and stopped prematurely, so I had to change the last if statement.
<div class="bs-news-wrapper">
{% for a in articles %}
{% cycle 'start' '' '' as slidestart silent %}
{% cycle '' '' 'end' as slidend silent %}
{% if slidestart %}<div class="item{% if forloop.first %} active {% endif %}">
<div class="row">{% endif %}
<div class="col-md-4">
#content
</div>
{% if not slidend and forloop.last or slidend %}
</div>
</div>{% endif %}
{% endfor %}
</div>
The change is in this line:
{% if not slidend and forloop.last or slidend %}
I hope this helps some future dwellers.

Jinja2: Create new row for every 3 items

Currently for every article in articles a new div with class span4 is created.
Instead for each row I would like to limit it's content to three span's and create a new row once that limit has been reached. How can I best implement this?
{% extends "base.html" %}
{% block content %}
<div class="container-fluid">
<legend></legend>
<div class="row-fluid" id="main">
{% for article in articles %}
<div class="span4">
<div class="thumbnail">
<img src="http://placehold.it/300x150/483CB4">
<div class="caption">
<h4>{{ article.title }}</h4>
<p> {{ article.summary }}</p>
</div>
<legend></legend>
<a class="btn" href="#"><i class="icon-thumbs-up"></i></a>
<a class="btn" href="#"><i class="icon-thumbs-down"></i></a>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
Goal:
<div class="row">
<div class="span4">article[0]</div>
<div class="span4">article[1]</div>
<div class="span4">article[2]</div>
</div>
<div class="row">
<div class="span4">article[3]</div>
<div class="span4">article[4]</div>
<div class="span4">article[5]</div>
</div>
The best way to do this is to use the built in batch filter to break up your list into groups of three:
{% for article_row in articles | batch(3, ' ') %}
<div class="row">
{% for article in article_row %}
<div class="span4">{{ article }}</div>
{% endfor %}
</div>
{% endfor %}
You can use loop.index0 and loop.last inside the for loop. Here is the for-loop documentation.
Example:
{% extends "base.html" %}
{% block content %}
<div class="container-fluid">
<legend></legend>
<div class="row-fluid" id="main">
{% for article in articles %}
{% if loop.index0 % 3 == 0 %}
<div class="row">
{% endif %}
<div class="span4">
...
</div>
{% if loop.index0 % 3 == 2 or loop.last %}
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endblock %}
The loop.last clause should close the last row even if there were less than 3 items. <div> should start when loop.index0 gives 0 as the remainder and should end when 2 is the remainder
Another alternative would be to group the items into rows before passing them into the template, then you can just issue two for-loops one inside the other.