Django Templates: how flexible are variables? - django

I need to have a variable in a teplate that is basically a counter for a for-loop. The problem is: I need to manipulate it, depending on the for-element I am dealing with, I will have to reset the counter (an IF inside the for-loop).
Is this doable inside a Django template?
This is basically what I would like:
{% i = 0 %}
{% for l in list %}
{% if i == 5 %}
{% i = 0 %}
Do Something
<br>
{% else %}
{% i = i + 1 %}
{% endif %}
{% endfor %}

You can't with the builtin tags:
http://www.mail-archive.com/django-users#googlegroups.com/msg27399.html
The following snippets might be a good starting point:
Template counters
Access by index
EDIT: For the record, OP needed a conditional with divisibleby. See the accepted answer here plus the comments in this answer.

What you want is the forloop.counter variable that Django's template language provides.
https://docs.djangoproject.com/en/dev/ref/templates/builtins/#for
You would do something like:
{% for element in list %}
{% if forloop.counter > 5 %}
Do something
{% else %}
Do something else
{% endif %}
{% endfor %}
If you want to do something cyclically, you're basically doing a modulo operator (http://en.wikipedia.org/wiki/Modulo_operation), unfortunately, Django Template doesn't quite have this, but it does allow a 'divisible by' operator.
https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#divisibleby
So you'll add:
{% if {{ forloop.counter|divisibleby:"5" }} %}
{{ whatever }}
{% endif %}

Related

If statement in my Django template...Is there a better way?

In my Django template: I'm trying to add an extra div around my for loop only if the length of the data being passed to the template is 3. This is what I'm trying right now but it seems like there could be better way than doing two if statements to check for the length:
{% if items|length == 3 %}
<div class='three-item-wrap'>
{% endif %}
{% for item in items %}
.......
{% endfor %}
{% if items|length == 3 %}
</div> //close .three-item-wrap
{% endif %}
you can try like that
{% if items|length == 3 %}
<div class='three-item-wrap'>
{% for item in items %}
.......
{% endfor %}
</div>
{% else %}
#another logic goes here
{% endif %}
if you want know more refer the docs django if tempalate
I think better way would be to make single if statement check. Just like this:
{% if items|length == 3 %}
<div class='three-item-wrap'>
{% for item in items %}
.......
{% endfor %}
</div>
{% else %}
{% for item in items %}
.......
{% endfor %}
{% endif %}
This way is better because of Django render engine, which firstly check if statements and then do for loop.
And if something crash in your code, div will be without closing tag </div>. Instead in my code there is no option for div to be without closing tag.

Am I using the django if template tag wrong?

I have this in my Django 1.2 template:
{% for m in model.related.iterator %}
{% if "str" or "Str" in m %}
{{m}}//I don't want anything here but put this in so I could see the output
{%else%}
{% if forloop.last %}
{% customtag "var" %}
{% endif %}
{% endif %}
{% endfor %}
I get the value of {{m}} no matter if it contains "str", "Str" or neither of them. Also, if I take out the or and compare one string it works but I would like to compare both without another else if. Any way to do that?
I believe what you want is
{% if "str" in m or "Str" in m %}
however, Glyn's answer would also work
try...
{% if "STR" in m|upper %}
Do something
{% endif %}

Django {% with %} tags within {% if %} {% else %} tags?

So I want to do something like follows:
{% if age > 18 %}
{% with patient as p %}
{% else %}
{% with patient.parent as p %}
...
{% endwith %}
{% endif %}
But Django is telling me that I need another {% endwith %} tag. Is there any way to rearrange the withs to make this work, or is the syntactic analyzer purposefully carefree in regards to this sort of thing?
Maybe I'm going about this the wrong way. Is there some sort of best practice when it comes to something like this?
if you want to stay DRY, use an include.
{% if foo %}
{% with a as b %}
{% include "snipet.html" %}
{% endwith %}
{% else %}
{% with bar as b %}
{% include "snipet.html" %}
{% endwith %}
{% endif %}
or, even better would be to write a method on the model that encapsulates the core logic:
def Patient(models.Model):
....
def get_legally_responsible_party(self):
if self.age > 18:
return self
else:
return self.parent
Then in the template:
{% with patient.get_legally_responsible_party as p %}
Do html stuff
{% endwith %}
Then in the future, if the logic for who is legally responsible changes you have a single place to change the logic -- far more DRY than having to change if statements in a dozen templates.
Like this:
{% if age > 18 %}
{% with patient as p %}
<my html here>
{% endwith %}
{% else %}
{% with patient.parent as p %}
<my html here>
{% endwith %}
{% endif %}
If the html is too big and you don't want to repeat it, then the logic would better be placed in the view. You set this variable and pass it to the template's context:
p = (age > 18 && patient) or patient.parent
and then just use {{ p }} in the template.

Interpolate Django template include variable

I'm doing something like
{% for part in parts %}
{% include "inc.html" with o=part prefix="part{{ forloop.counter0 }}_" %}
{% endfor %}
where inc.html could be something of such kind:
<p id="{{ prefix }}para">{{ o.text }}</p>
I just discovered the prefix variable isn't interpolated and "part{{ forloop.counter0 }}_" is passed literally.
Any relatively elegant work-around?
I think the best solution would be to register an inclusion_tag, that would handle the part and forloop.counter operations:
#register.inclusion_tag("inc.html")
def inc_tag(part, loop_counter):
prefix = 'part%s_' % (loop_counter,)
context = {
'part': part,
'prefix': prefix,
}
return context
And you would call it like that
{% for part in parts %}
{% inc_tag part=part loop_counter=forloop.counter0 %}
{% endfor %}
Your way is also doable like so, but I wouldn't recommend that
{% for part in parts %}
{% with "part"|add:forloop.counter0|add:"_" as prefx %}
{% include "inc.html" with o=part prefix=prefix %}
{% endwith %}
{% endfor %}

Is there any way to reuse template tag value in Django templates?

For example, let's say there is a custom template tag
{% custom_tag "parameter" %}
This tag requires some serious database work to calculate.
Now I need to have something like that (pseudocode):
if {% custom_tag "parameter" %}
....
else
....
I know that with context variables I can do just:
{% with variable.x.y.z as v %}
{% if v %}
Blah-Blah-Blah {{ v }}
{% else %}
No value
{% endif %}
{% endwith %}
But is there any way do achieve this with template tag value?
EDIT:
The only option I've came up with so far is to make a filter out of my template tag:
{% if "parameter" | custom_tag %}
Blah {{ "parameter" | custom_tag }}
{% else %}
....
{% endif %}
But this option makes custom_tag execute twice, and that's not good performance-wise
i haven't test it but i guess that you can add a variable to the context from your custom tag.. maybe this will help you
http://docs.djangoproject.com/en/dev/howto/custom-template-tags/#setting-a-variable-in-the-context
I believe you can assign the results of filtering to a variable and use it. This way the filter will only get called once. From the docs: with: Caches a complex variable under a simpler name. This is useful when accessing an "expensive" method (e.g., one that hits the database) multiple times.
{% with "parameter" | custom_tag as result %}
{% if result %}
Blah {{ result }}
{% else %}
....
{% endif %}
{% endwith %}