Is a django template forloop resetabble? - django

Is it possible to reset the forloop.counter object back to zero?
I'm using it to spit out objects, but when the object type changes I want to zero it, so that I can count to 5 objects and output an end of row object (with no right padding) as the fifth and only fifth object in that type.
And if I've got 5 objects, the first two are type a and the next 3 are type b, the fifth one will always get the smaller padding, never mind that its only the third on the line.
Here is my template code:
{% for project in projects %}
{% ifchanged project.proj_type %}
{% forloop.counter=="1" %}
</div>
<div class="span-4 prepend-top">
<h5 class="right" >{{ project.proj_type }}</h5>
</div>
<div class="span-19 append-1 last" id="row-of-projects">
{% endifchanged %}
{% if forloop.counter|divisibleby:"4" %}
<div class="span-4 append-1 prepend-top last" id="project">
{% else %}
<div class="span-4 append-1 prepend-top" id="project">
{% endif %}
<p class="project-name">{{ project.name }}</p>
<a href="/gallery/{{ project.slug }}/" ><img src="/media/pa/photographs/{{ project.get_photograph }}-t.jpg" alt="{{ project.name }}" /></a>
</div id="project">
{% endfor %}

It sounds like you should be regrouping objects based on type, and doing the padding in the inner loop.

I'm not quite sure that I understand what you need.
You can use the regroup tag to display your objects according to type.
And you can use class="{% cycle 'normal' 'normal' 'normal' 'normal' 'special' %}" to style every fifth element differently.

Related

Django iteration (for ... in ...) how to put it in different divs?

I have few instances of model.
my model:
class Record(models.Model):
name = models.ForeignKey(Car)
image = models.ImageField(upload_to='images/')
created = models.DateTimeField(
default=timezone.now)
view:
def allrecords(request):
records = Record.objects.all().order_by('created')
return render(request, 'mycar/allrecords.html', {'records' : records})
I want show it on my website. In my template i have:
{% for record in records %}
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endfor %}
Now i get list of my records, but i would like put the newest record to first div, next to second etc. How can i do that?
I show simple screen how i would like have that (if someone will create new record, it will go to first div and other records will change place. Is any possibility to do something like that?
edit:
<div>
{% for record in records %}
{% if forloop.counter == 1 %}
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endif %}
{% endfor %}
</div>
<div>
{% for record in records %}
{% if forloop.counter == 2 %}
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endif %}
{% endfor %}
</div>
.
.
# till your 5th image
You can use forloop.counter to get the iteration number and check what is the iteration the loop and handle data accordingly.
In addition you can use CSS to make the layout work as you want.
Here is the information for Django template counter
Edit :
{% for record in records %}
<div>
{% if forloop.counter == 1 %}
# Here you can get your first images
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endif %}
</div>
<div>
{% if forloop.counter == 2 %}
# Here you can get your first images
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endif %}
</div>
.
.
# till your 5th image
{% endfor %}
There are two ways to do this. If you want to set this option for a single view then:
def all_records(request):
records = Record.objects.all().order_by('-created')
return render(request, 'mycar/allrecords.html', {'records' : records})
You're almost correct but order_by('created') leads to asceding order while order_by('-created') leads to descending order which is what you require.
Alternatively, if you want to have this setting to apply to all views then set class Meta in your models.py which will ensure that wherever you use Record.objects.all() it returns Records in descending order of created field:
class Record(models.Model):
name = models.ForeignKey(Car)
image = models.ImageField(upload_to='images/')
created = models.DateTimeField(
default=timezone.now)
class Meta:
ordering = ('-created')
It's Django design pattern to make all logical decisions in models and views and only just plugin formatted data in templates. You shouldn't add any complex logic in templates.
I'm assuming the question means that the model might have more than 5 records. If so, a more generic solution would be
<div class='row'>
<div class='firstimage'>
<img src={{ records[0].image.url}}/>
{{record.name}}
</div>
{% for record in records %}
{% if forloop.counter > 1 %}
<div class='subsequentimage'>
<img src={{ record.image.url}}/>
{{record.name}}
</div>
{% endif %}
{% cycle "" "</div><div class='row'>" "" %}
{% endfor %}
</div>
Note the use of the 'cycle' tag to begin a new row div every third cell div.
I don't know what your CSS classes are to distinguish between rows and cells so I used 'row', 'firstimage' (which might be defined to take up twice as much width) and 'subsequentimage' as example classes.
I recommend you to use the context variables:
def all_records(request):
records = Record.objects.all().order_by('-created')
newest = records[:5]
oldest = records[5:]
return render(request, 'mycar/allrecords.html', {'newst' : newest,
'oldest': oldest })
In your template :
{% for new in newst %}
<div>what you want with news</div>
{% endfor %}
{% for old in oldest %}
<div>what you want with olds</div>
{% endfor %}

Checking against a unicoded list of strings in template

I have the following code in my template (please note the if statement):
{% for base in bases %}
<label class="checkbox">
<input name="base" value={{ base.id }} type="checkbox"
{% if base.id in selected_bases %}checked="checked" {% endif %}/>
<span>{{ base.name }}</span>
</label>
{% endfor %}
The selected_bases variable is a list of unicoded strings: [u'3', u'1', u'5'].
base.id is an integer.
How can I make them the same type so that if statement does what I need it to?
I don't know if this work, but try this:
{% if value|stringformat:"d" in selected_bases %}
You should probably do this in the view instead, but you can pipe the list values through the add filter, which does type coercion - or pipe the ints to slugify, which will do the reverse. More information here.

How to iterate this dictionary of lists of lists in Django?

I have the below dictionary with lists of objects.
args = {'Qtag': [
[<Question: Question object>, <Tags: Tags object>],
[<Question: Question object>, <Tags: Tags object>]
]
}
inside my views.py
args=dict(Qtag=Questag)
t=loader.get.template('main.html')
c=Context(args)
and inside my main.html, I am trying to iterate the below way
{%if Qtag %}
{% for item_list in Qtag %}
{% for item in item_list %}
</h4> <b> Question</b>:{{item.qid}} {{item.title}} </h4><br/>
</h4> {{item.question}} </h4>
<button name="tag" type="submit" value="tagname"> {{item.tagname}} </button>
{% endfor %}
{% endfor %}
{% endif %}
When I tried the above way in my main.html, I am getting the things displayed 4 times in the screen. Here, the first list consisting of list of Question object and list of tag object corresponds to a single question and similarly for the other one, so in the screen I only want the two question and its corresponding tags to be displayed. I am not able to iterate properly to get what I need. Any thought on how to achieve this!!!
Thanks.
I'm not sure what args=dict(Qtag=Questag) means in your code (since I don't know what Questag is). I don't know whether your tags are iterable either (I'm assuming so). Your HTML looks malformed too (e.g. I don't see an opening h4).
This is what I'd do to get something similar to what you're looking for. You can work on this.
{% if Qtag %}
{% for question, tags in Qtag %}
{# First put in the question header #}
<h4>Question : {{question.qid}} {{question.title}} </h4>
{# Then the question body #}
<p>
{{question.question}}
</p>
{# Now a tag list #}
<ul>
{% for tag in tags %}
<li>
<button name = "tag" type="submit" value="{{tag.name}}">
{{tag.name}}
</button>
</li>
{% endfor %}
</ul>
{% endfor %}
{% endif %}
Your original solution is iterating over the list quadratically (you're iterating over the list once for every outer iteration) and that's why you're seeing things being printed 4 times.

How do you limit list objects template side, rather than view side

One of the ways to limit objects is to add a limitation to a function like this
def ten_objects():
obj = Model.objects.all()[0:10] # limit to 10
return {'objects': obj}
However how do you achieve this inside a template rather than inside a view?
I know you can filter through objects within a template and limit characters but how do you actually limit the amount of objects displayed in a loop. Via the template. for example the following code will loop through all objects....
<ul>
{% for new in news %}
<li>
<p>{{ news.title }}</p>
<p>{{ news.body }}</p>
</li>
{% endfor %}
</ul>
How do I break the loop, say after the firs 3 objects/items? And how do I do it inside the template?
There is a slice filter that you can use in templates. This works exactly the same as slicing within the view.
{% for new in news|slice:":10" %}
You want to use the slice template filter
Here's your example altered to use it:
<ul>
{% for new in news|slice:":3" %}
<li>
<p>{{ new.title }}</p>
<p>{{ new.body }}</p>
</li>
{% endfor %}
</ul>

Alternate Row Coloring in Django Template with More Than One Set of Rows

Django templates offer the builtin tag cycle for alternating between several values at different points in a template (or for loop in a template) but this tag does not reset when it is accessed in a scope outside of the cycles definition. I.e., if you have two or more lists in your template, the rows of all of which you'd like to use some css definitions odd and even, the first row of a list will pick up where the last left off, not with a fresh iteration from the choices (odd and even)
E.g., in the following code, if the first blog has an odd number of entries, then the first entry in a second blog will start as even, when I want it to start at odd.
{% for blog in blogs %}
{% for entry in blog.entries %}
<div class="{% cycle 'odd' 'even' %}" id="{{entry.id}}">
{{entry.text}}
</div>
{% endfor %}
{% endfor %}
I've tried obviating this by patching with the resetcycle tag offered here:
Django ticket: Cycle tag should reset after it steps out of scope
to no avail. (The code didn't work for me.)
I've also tried moving my inner loop into a custom tag, but this also did not work, perhaps because the compile/render cycle moves the loop back into the outer loop? (Regardless of why, it didn't work for me.)
How can I accomplish this simple task!? I'd prefer not to create a data structure in my view with this information pre-compiled; that seems unnecessary. Thanks in advance.
The easiest workaround (until the resetcycle patch gets fixed up and applied) is to use the built-in "divisibleby" filter with forloop.counter:
{% for entry in blog.entries %}
<div class="{% if forloop.counter|divisibleby:2 %}even{% else %}odd{% endif %}" id="{{ entry.id }}">
{{ entry.text }}
</div>
{% endfor %}
A little more verbose, but not hard to understand and it works great.
https://docs.djangoproject.com/en/1.8/ref/templates/builtins/#cycle
{% for o in some_list %}
<tr class="{% cycle 'row1' 'row2' %}">
...
</tr>
{% endfor %}
Give up and use Jinja2 Template System
I gave up on django template language, it's very restricted in what you can do with it.
Jinja2 uses the same syntax that the django template uses, but adds many enhancements over it.
EDIT/NOTE ( I know it sounds like a big switch for just a minor issue, but in reality I bet you always find yourself fighting the default template system in django, so it really is worthwhile and I believe it will make you more productive in the long run. )
You can read this article written by its author, although it's technical, he mentions the problem of the {% cycle %} tag in django.
Jinja doesn't have a cycle tag, it has a cycle method on the loop:
{% for user in users %}
<li class="{{ loop.cycle('odd', 'even') }}">{{ user }}</li>
{% endfor %}
A major advantage of Jinja2 is that it allows you to use logic for the presentation, so if you have a list of pictures, you can put them in a table, because you can start a new row inside a table every N elements, see, you can do for example:
{% if loop.index is divisibleby(5) %}
</tr>
{% if not loop.last %}
<tr>
{% endif %}
{% endif %}
you can also use mathematical expressions:
{% if x > 10 %}
and you can access your python functions directly (but some setup is required to specify which functions should be exposed for the template)
{% for item in normal_python_function_that_returns_a_query_or_a_list() %}
even set variables ..
{% set variable_name = function_that_returns_an_object_or_something() %}
You can use tagged cycle and resetcycle (new in Django 1.11) calls (from https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#std:templatetag-resetcycle ):
{% for blog in blogs %}
{% cycle 'odd' 'even' as rowcolors silent %}
{% resetcycle rowcolors %}
{% for entry in blog.entries %}
{% cycle rowcolors %}
<div class="{{ rowcolors }}" id="{{entry.id}}">
{{ entry.text }}
</div>
{% endfor %}
{% endfor %}
I end up doing so, with the forloop.counter0 - It works great!
{% for product in products %}
{% if forloop.counter0|divisibleby:4 %}<div class="clear"></div>{% endif %}
<div class="product {% if forloop.counter0|divisibleby:4 %}col{% else %}col20{% endif %}">
Lorem Ipsum is simply dummy text
</div>
{% endfor %}
The easiest answer might be: "give up and use jQuery." If that's acceptable it's probably easier than fighting with Django's templates over something so simple.
There's a way to do it server-side with an iterator that doesn't keep a simultaneous copy of all the entries:
import itertools
return render_to_response('template.html',
{
"flattened_entries": itertools.chain(*(blog.entries for blog in blogs)),
})