Python Jinjna variable doesnt keep value outside loop - python

I am currently trying to fix an issue by using jinja variables, but somehow the variable does not keep the value outside the loop, even though I declared it before the loop begins:
{% set disablecounter = 0 %}
{% if disablecounter == 0 %}
{% for einzelroom in all_einzelzimmer %}
{% if zimmer.id == einzelroom.zimmer_id %}
{% set price = einzelroom.preis %}
<div class="preis-element">
<p class="preis"> <span class="smallab"> ab </span> {{ price|int }}€ </p>
</div>
{% set disablecounter = disablecounter + 1 %}
{{ disablecounter }}
{% endif %}
{% endfor %}
{% endif %}
{{ disablecounter }}
The variable is disablecounter inside the loop it is 1 but outside it is still 0
Thanks!
EDIT
Surrounding with a with statement also didnt worked:
{% with foo = 42 %}
{{ foo }}
{% endwith %}
{% with %}
{% set foo = 42 %}
{{ foo }}
{% endwith %}

I found a great solution here on SO by #Chris Warth.
Original answer by #Peter Hollingsworth:
https://stackoverflow.com/a/32700975/5291566
{% with disablecounter = [0] %}
{% if disablecounter == [0] %}
{% for einzelroom in all_einzelzimmer %}
{% if zimmer.id == einzelroom.zimmer_id %}
<div class="preis-element">
<p class="preis"> <span class="smallab"> ab </span> {{ einzelroom.preis|int }}€ </p>
</div>
{% if disablecounter.append(disablecounter.pop() + 1) %}{% endif %}
{% endif %}
{% endfor %}
{% endif %}

Related

Forloop.counter in Django

{% for each_item in item.artifacts %}
{% if each_item.scanner_count > 0 and each_item.scanner_match > 0 %}
{% if forloop.counter <= 5 %}
<tr>
<td>{{each_item.threat_name}}</td>
</tr>
{% else %}
{% if forloop.last %}
<p><b><i> {{ forloop.counter|add:"-5" }} rows were truncated. See full report for more details. </i></b></p>
{% endif %}
{% endif %}
{% else forloop.counter -=1 %}
{% endif %}
{% endfor %}
ERROR:Malformed template tag at line 171: "else forloop.counter -=1"
I want to increment the counter only when if condition is successful. Dont know how to do it with forloop.counter. Goal is to print 5 rows of valid output(scanner count >0 and Scanner match >0)
You can use combination of add and forloop counter to achieve what you want to achieve. But remember you need to pass some variable (I've used rank here for kinda storing the increment variable in template.
views.py
rank = 0
return render(request, "base.html", {"items": items, "rank": rank})
html
{% for each_item in items %}
{% if each_item.scanner_count > 0 and each_item.scanner_match > 0 %}
{% if forloop.counter|add:rank <= 5 %}
<tr><td>{{each_item.threat_name}}</td></tr>
<br>
{% else %}
{% if forloop.last %}
<p><b><i> {{ forloop.counter|add:"-5" }} rows were truncated. See full report for more details. </i></b></p>
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
As other commenters have noted, you cannot make an assignment of the forloop.counter in the template, as that would be considered logic that should be in the view (controller). Remove the {% else forloop.counter -= 1 %} from your code and it should work as I think you intended. If not, either add logic in the object (context) being passed to the template or user other forloop attributes/variables as found in the Django documentation here: https://docs.djangoproject.com/en/3.0/ref/templates/builtins/#for

How to write a condition inside a jinja expression

I have a jinja expression as:
{% if meaning['example'] %}
{{ meaning['example'] }}<br>
{% endif %}
Which gives me o/p:
you don't know jack
Here word['word'] has the value jack in the o/p
How can I make the jack bold in the expression?
WRT SO I have tried as:
{{ <strong>word['words']</strong> if word['words'] in meaning['example'] }}
But this didn't work , Any help is much appreciated , TIA
{% if meaning['example'] %}
{% for w in meaning['example'] %}
{% if w == word['word']%}
<strong>{{w}} </strong>
{% else %}
{{w}}
{% endfor %}
{% endif %}
if you have number of word then replace
{% if w == word['word']%} -->> {% if w in word %}
You have a couple of options:
in an if block
{% if word['words'] in meaning['example'] %}
<strong>word['words']</strong> }}
{% endif %}
in a ternary if expression
{{ '<strong>' + word['words'] + '</strong>' if word['words'] in meaning['example'] else '' }}
I personally prefer the first option if I want to write markup, or the conditional expression is relatively long as in your example.
For anyone's ref
Thanks for #Mayank Jain comment from where i digged this solution for my Q :
{% if meaning['example'] %}
{% for w in meaning['example'].split(' ') %}
{% if w == word['word']%}
<strong>{{w}} </strong>
{% else %}
{{w}}
{% endif %}
{% endfor %}
{% endif %}

I cannot show the value in html

I cannot show the value in html.
I wrote in views.py
def score_test(request):
results = TestScore.objects.filter(user=request.user).all()
print(results)
return render(request, 'score_test.html',{'results': results})
in score_test.html
<div class="col-xs-12">
<h2 class="result_title">Test Score</h2>
<h3>Your score is
 {% for result in results.all %}
{% if result.score > 95 %}
<h4 class="one"> {{ result.score }} </h4>
{% else %}
{% if result.score > 80 %}
<h4 class="two"> {{ result.score }} </h4>
{% else %}
{% if result.score > 60 %}
<h4 class="three"> {{ result.score }} </h4>
{% else %}
{% if result.score > 40 %}
<h4 class="four"> {{ result.score }} </h4>
{% else %}
<h4 class="five"> {{ result.score }} </h4>
{% endif %}
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
</h3>
</div>
in models.py
class TestScore(models.Model):
score = models.FloatField(null=True, blank=True, default=None)
In print(results) of views.py, I got <function tc at 0x105a15d70>, so I think I can get score of TestScore model in results. So, how can I fix this? Am I wrong to write if-else statement in template of score_test.html?
"function tc at 0x105a15d70" is definitely not what TestCore.objects.filter() should return (it should be a queryset), and certainly not what the code you posted could yield, so no surprise you don't see anything in your template.
This being said there are a couple possible improvements to your code...
First, in your view:
results = TestScore.objects.filter(user=request.user).all()
the .all() is redundant. You just need:
results = TestScore.objects.filter(user=request.user)
You could even just use the reverse relation (assuming you didn't specify a related_name in your TestScore.user field):
results = request.user.testscore_set.all()
Then in your templates:
 {% for result in results.all %}
The ".all" is once again useless, and possibly (or not) the cause of your problem. But well, it's useless so don't use it ;)
 {% for result in results %}
Then you could use {% elif <condition> %} instead of nested{% else %}{% if %}`:
{% if result.score > 95 %}
<h4 class="one"> {{ result.score }} </h4>
{% elif result.score > 80 %}
<h4 class="two"> {{ result.score }} </h4>
{% elif result.score > 60 %}
<h4 class="three"> {{ result.score }} </h4>
{% elif result.score > 40 %}
<h4 class="four"> {{ result.score }} </h4>
{% else %}
<h4 class="five"> {{ result.score }} </h4>
{% endif %}
But using a custom template filter would make your code simpler:
# <yourapp>/templatetags/<yourlibname>.py
from django import template
register = template.Library()
#register.filter
def score_grade(score):
try:
score = int(score)
except (TypeError, ValueError):
# XXX should at least log the exception
return ""
if score > 95:
return "one"
elif score > 80:
return "two"
elif score > 60:
return "three"
elif score > 40:
return "four"
else:
return "five"
and in your template:
{% load <yourlibname> %}
{% for result in results %}
<h4 class="{{ result.score|score_grade }}"> {{ result.score }} </h4>
{% endfor %}
You can use elif command.
{% if result.score > 95 %}
<h4 class="one"> {{ result.score }} </h4>
{% elif result.score > 80 %}
<h4 class="two"> {{ result.score }} </h4>
...
{% else %}
<h4 class="five"> {{ result.score }} </h4>
{% endif %}

Loop over dictionary of dictionaries in Django Template for 1 line of code

The dictionary I'm passing to the Django template contains 2 dictionaries, each with a list:
'nav_dict': {
'class_name': ['Chemical', 'Avian', 'Mammal'],
'tab_label': ['Chemical!', 'Avian!', 'Mammal!']
}
I want to loop over the lists in each dict to fill out this line of code:
<li class="{{ item_className }} tabSel">{{ item_tabLabel }}</li>
where item_className = each value in the class_name list and item_tabLabel = each value in the tab_label list. The result would 3 <li> tags with a class_name and tab_label.
I have tried something like this (This code only handles the class_name part), but I can't get the loops to append to the same line of code (each <li>):
{% for key, value in nav_dict.items %}
{% if key == 'class_name' %}
{% for item_className in value %}
{% if forloop.counter0 == 0 %}
<li class="{{ item_className }} tabSel">{{ item_tabLabel }}</li>
{% else %}
<li class="{{ item_className }} tabUnsel">{{ item_tabLabel }}</li>
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
The problem you have is with the way you are presenting your data.
Why don't you create your dictionary like this, where each key is the class name and the associated value is the label. It would be more semantic and readable.
'nav_dict': {
'Chemical': 'Chemical!',
'Avian': 'Avian!',
'Mammal': 'Mammal!,
}
Then you can just loop over this and place the key as the class attribute, and the value as the label attribute.
{% for key, value in nav_dict.items %}
{% if forloop.counter0 == 0 %}
<li class="{{ key }} tabSel">{{ value }}</li>
{% else %}
<li class="{{ key }} tabUnsel">{{ value }}</li>
{% endif %}
{% endfor %}
If the order of the elements is important (as dictionaries are un-ordered), you could use an OrderedDict from the collections module to retain insertion order. You would do something like this inside your view...
>>> from collections import OrderedDict
>>> nav_dict = OrderedDict(zip(['Chemical', 'Avian', 'Mamma!'], ['Chemical!', 'Avian!', 'Mammal!']))
OrderedDict([('Chemical', 'Chemical!'), ('Avian', 'Avian!'), ('Mammal', 'Mammal!')])
Similarly you could use a list of tuples as your data
'nav_list': [('Chemical', 'Chemical!'), ('Avian', 'Avian!'), ('Mammal', 'Mammal!')]
And loop over this object like so
{% for class_name, label_name in nav_list %}
{% if forloop.counter0 == 0 %}
<li class="{{ class_name }} tabSel">{{ label_name }}</li>
{% else %}
<li class="{{ class_name }} tabUnsel">{{ label_name }}</li>
{% endif %}
{% endfor %}
If you only want to append an exclamation mark to your string to create the label, you could of course create a custom template filter instead - or just append the exclamation mark inside the template itself.
If you have to use that dictionary, you can use this template code:
{% for item_className in nav_dict.class_name %}
{% with forloop.counter0 as index_className %}
{% for item_tabLabel in nav_dict.tab_label %}
{% with forloop.counter0 as index_tabLabel %}
{% ifequal index_className index_tabLabel %}
{% if index_className == 0 %}
<li class="{{ item_className }} tabSel">{{ item_tabLabel }}</li>
{% else %}
<li class="{{ item_className }} tabUnsel">{{ item_tabLabel }}</li>
{% endif %}
{% endifequal %}
{% endwith %}
{% endfor %}
{% endwith %}
{% endfor %}

Jinja2: Change the value of a variable inside a loop

I want to change the value of the variable declared outside the loop within a loop. But always changing, it keeps the initial value outside the loop.
{% set foo = False %}
{% for item in items %}
{% set foo = True %}
{% if foo %} Ok(1)! {% endif %}
{% endfor %}
{% if foo %} Ok(2)! {% endif %}
This renders:
Ok(1)!
So the only (bad) solution have found so far was this:
{% set foo = [] %}
{% for item in items %}
{% if foo.append(True) %} {% endif %}
{% if foo %} Ok(1)! {% endif %}
{% endfor %}
{% if foo %} Ok(2)! {% endif %}
This renders:
Ok(1)!
Ok(2)!
But, its is very ugly! Is there another more elegant solution?
Try also dictionary-based approach. It seems to be less ugly.
{% set vars = {'foo': False} %}
{% for item in items %}
{% if vars.update({'foo': True}) %} {% endif %}
{% if vars.foo %} Ok(1)! {% endif %}
{% endfor %}
{% if vars.foo %} Ok(2)! {% endif %}
This also renders:
Ok(1)!
Ok(2)!
as mentioned in the documentation:
Please note that assignments in loops will be cleared at the end of
the iteration and cannot outlive the loop scope.
but as of version 2.10 you can use namespaces:
{% set ns = namespace(foo=false) %}
{% for item in items %}
{% set ns.foo = True %}
{% if ns.foo %} Ok(1)! {% endif %}
{% endfor %}
{% if ns.foo %} Ok(2)! {% endif %}
You could do this to clean up the template code
{% for item in items %}
{{ set_foo_is_true(local_vars) }}
{% if local_vars.foo %} Ok(1)! {% endif %}
{% endfor %}
{% if local_vars.foo %} Ok(2)! {% endif %}
And in the server code use
items = ['item1', 'item2', 'item3']
#---------------------------------------------
local_vars = { 'foo': False }
def set_foo_is_true(local_vars):
local_vars['foo'] = True
return ''
env.globals['set_foo_is_true'] = set_foo_is_true
#---------------------------------------------
return env.get_template('template.html').render(items=items, local_vars=local_vars)
This could be generalized to the following
{{ set_local_var(local_vars, "foo", False) }}
{% for item in items %}
{{ set_local_var(local_vars, "foo", True) }}
{% if local_vars.foo %} Ok(1)! {% endif %}
{% endfor %}
{% if local_vars.foo %} Ok(2)! {% endif %}
And in the server code use
items = ['item1', 'item2', 'item3']
#---------------------------------------------
local_vars = { 'foo': False }
def set_local_var(local_vars, name, value):
local_vars[name] = value
return ''
env.globals['set_local_var'] = set_local_var
#---------------------------------------------
return env.get_template('template.html').render(items=items, local_vars=local_vars)

Categories