if-statement doesn't work in for-loop - django

I have an issue with the if-statement and need some help. This is a short code-snippet in html:
{% for category in categories %}
{% if category == 'christmas' %}
<p>{{category}} 1</p>
{% else %}
<p>{{category}} 2</p>
{% endif %}
{% endfor %}
With the for-loop I walk through categories and check them with an if-statement for the string 'christmas'. The paragraph is always the second one, ending with a 2. Still, it appears that the category with the name 'christmas' comes up. That means, that in the if-statement "category" is different than in the p-tag. In fact, category is empty in the if-statement.
Why? Can someone help here, please. Thanks!
Edit: Added two pictures. On the right you see the output:
length is zero
showing no fit, although it should

I'm guessing you're looping over category objects from a model meaning categories is not a list of strings but a queryset? In that case you should do something like this:
{% for category in categories %}
{% if category.name == 'christmas' %}
<p>{{category}} 1</p>
{% else %}
<p>{{category}} 2</p>
{% endif %}
{% endfor %}
Replace .name by whatever your correct attribute is.
Your {{category}} is probably displayed correctly because of your __unicode__ or __str__ method.

Related

How to get a certain number of elements in django template

I wonder how I can get a specific number of items when i put an if statement inside a for loop
i know we can do {% for i in items|slice ":5"%} to get a number of items but when i do
{% for post in posts %}
{% for img in post_imgs %}
{% if img.link == post.link %}
<img class="class" src="{{img.img.url}}" style="width:100%">
{% endif %}
{% endfor %}
{% endfor %}
there's no way of doing that inside the if tag .. any solution
From this answer:
Changing the state of an object in a Django template is discouraged.
You should probably bite the bullet, calculate the condition
beforehand and pass extra state to the template so you can simplify
the template logic.
So just do your comparisons in python in your view, something like:
post_imgs_filtered = [img for img in post_imgs if img.link == post.link]
And then in your template:
{% for img in post_imgs_filtered|slice ":5" %}
<img class="class" src="{{img.img.url}}" style="width:100%">
{% endfor %}

Django template if statement return wrong value

In my home page I have blog, on the right side there is Posts category list like:
Sports
Crime
and etc.. I try to make that when I'm in certain category that active category button will be highlighted.
I'm doing that by setting GET parameter to my home page like: /?category=1
NOW... In index.html template I'm doing this:
{% for category in category_list %}
{{ category.pk }} != {{ request.GET.category }} // This is for debugging. Returns 1==1
{% if category.pk == request.GET.category %}
<li>{{ category }}<span class="pull-right">({{ category.post_set.count }})</span></li>
{% else %}
<li class="active">{{ category }}<span class="pull-right">({{ category.post_set.count }})</span></li>
{% endif %}
{% endfor %}
But this is not working. Any suggestions how to solve this, maybe there is another way?
EDITED:
I figured that category.pk returns int and request.GET.category returns string. Next question. How to convert int<==>string, so I could compare them?
It worked by doing this:
{% if category.pk != request.GET.category|add:"0" %}
apparently |add:"0" converts string to int.

Django template length attribute behaves strange

I have encountered a weird behavior. And not sure how I should change it to get what I want.
Example:
{% if chapters|length > 0 %}
Chapters:
<div id="left_menu">
<ul>
{% for chapter in chapters %}
<li>{{ chapter }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
Sometimes however my queryset is empty.
When I get this length in the shell it returns 0. So I assumed this would work.
However, when the queryset chapters is empty it still shows my Chapters: and the div inside the page source.
What is going on here and how can I get the result I want?
chapters:
chapters = Chapter.objects.filter(campaign=campaign).order_by("number")
just a normal queryset that might be empty.

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.

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