What does this html tag mean in django's example? - django

This is django's polls demo, and most are well documented. However, in this part:
https://docs.djangoproject.com/en/3.0/intro/tutorial04/
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>
Vote again?
The documentation doesn't say anything about this part:
vote{{ choice.votes|pluralize }}
And from the generated html page, I can't see what's the role of this piece?

pluralize is an in-built Django template tag that attempts to convert the word that it is appended to to plural. So you feed it a number, and if the number is 1 then it returns '', but if the number is greater than 1, it returns 's'.
https://docs.djangoproject.com/en/3.0/ref/templates/builtins/#pluralize

Related

How to show the first true element after if condition in for loop in django template?

I have a for loop in Django template. After that, I check for coincidences. But in some cases, there are might be 3 coincidences. I need to show only the first coincidence. Now, my code returns the name for 3 times, because, there are 3 coincidences
{% for ip in ips %}
{% if d.name == ip.name %}
<strong>{{ d.name}} </strong>
{% endif %}
{% endfor %}
SOLUTION
It is impossible to break forloop in django template, so I decided to change in views.py through queryset distinction of similar names
ips = Point.objects.defer('point').order_by('name').distinct('name')
I don't recommend doing this in Django Template , but in views itself. But if you can't then you can use {{ forloop|break }}.
Something like this :
{% for ip in ips %}
{% if d.name == ip.name %}
{{ forloop|break }}
<strong>{{ d.name}} </strong>
{% endif %}
{% endfor %}
Check the small snippet example here...

Showing random objects in Django template

In my Django template as I am iterating through a list of objects, I'd like to have one list item say:
<li>Blah</li>
and then another do:
<li>Blah</li>
I see value|random as an option but for some reason this doesn't work:
{% ifequal [1, 2]|random 1 %}
{{ post.title }}
{% else %}
{{ post.title }}
{% endifequal %}
Doing this throws this error:
u'ifequal' takes two arguments
Is there any way to accomplish this? I would think it should be simple but I realize the Django templating language doesn't allow for variable assignments.
Thanks!
You can't put a list directly into the template like that, make_list is what you're after.
make_list returns a list of strings, so this would work.
{% if 12|make_list|random == '1' %}
<li>Blah</li>
{% else %}
<li>Blah</li>
{% endif %}

Variable subtraction in django templates

It is able to write {{ myval.add:5 }}, {{ myval|add:value }} and even {{ myval|add:-5 }}.
However, I can't find out what I should type to add value * -1 like {{ myval|add:-value }}. This doesn't work, sadly.
You need to use double quotes:
{{ myval|add:"-5" }}
This subtracts five from myval.
The built-in Django template tags/filters aren't all-encompassing, but it's super easy to write your own custom template tags: https://docs.djangoproject.com/en/dev/howto/custom-template-tags/
You could make your own subtract template tag pretty easily:
#register.filter
def subtract(value, arg):
return value - arg
Use django-mathfilters from PyPI: https://pypi.python.org/pypi/django-mathfilters
To install :
$ pip install django-mathfilters
Then add mathfilters in your INSTALLED_APPS.
In template:
{% load mathfilters %}
<ul>
<li>8 + 3 = {{ 8|add:3 }}</li>
<li>13 - 17 = {{ 13|sub:17 }}</li>
{% with answer=42 %}
<li>42 * 0.5 = {{ answer|mul:0.5 }}</li>
{% endwith %}
{% with numerator=12 denominator=3 %}
<li>12 / 3 = {{ numerator|div:denominator }}</li>
{% endwith %}
<li>|-13| = {{ -13|abs }}</li>
</ul>
I recently started working with Django and stumbled upon this one as well: I needed a very simple template loop that stops printing after n times and shows a "more" link to toggle the rest of the items.
With great interest I read the struggle of people trying to understand why this is not being added to the Django default filters (since before 2013). I didn't feel like creating a custom template tag and I kind of found a way to subtract 2 variables using strings and add in combination with with and stringformat
Let's say I have a list of items where I want to print the first 2 and hide the rest, showing how many hidden items are there, eg.
John, Anna and 5 others like this (when given a list of 7 items)
As long as the number of visible items is harcoded in the template (eg. 2), it's possible to add the negative 2 |add:"-2", but I wanted the number of visible items to be a variable as well. The Math-filter library as suggested above doesn't seem up to date (I haven't tested it with Django 2.x).
The trick seems to be to use the add helper to concat the strings "-" with the integer as string, so it can be coerced back to a negative integer in a any consecutive calls to the add helper. This doesn't work however if the value is not a string, so that's where the stringformat helper comes in.
With string value
template posts.html (note how visible is explicitely passed as string - alternative below)
{% for post in posts %}
<h4>{{ post.title }}</h4>
...
{% include 'show_likes.html' with likes=post.likes visible="3" %}
{% endfor %}
template show_likes.html (note the add:0 to make the boolean operator work)
{% with show=visible|default:"2" %}
{% for like in likes %}
{% if forloop.counter <= show|add:0 %}
{% if not forloop.first %},{% endif %}
{{ like.username }}
{% endif %}
{% endfor %}
{% if likes|length > show|add:0 %}
{% with rest="-"|add:show %}
and {{ likes|length|add:rest }} more
{% endwith %}
{% endif %}
like this
{% endwith %}
Alternative with integer
You could just convert your integer to a string in the calling template using |stringformat:"d"
If however the number of visible items you want to show is an integer, you'll have to add a call to stringformat:"d" to have it converted to string
template posts.html
{% for post in posts %}
<h4>{{ post.title }}</h4>
...
{% include 'show_likes.html' with likes=post.likes visible=3 %}
{% endfor %}
template show_likes.html
{% with show=visible|default:2 %}
{% with show_str=show|stringformat:"d" %}
{% for like in likes %}
{% if forloop.counter <= show %}
{% if not forloop.first %},{% endif %}
{{ like.username }}
{% endif %}
{% endfor %}
{% if likes|length > show|add:0 %}
{% with rest="-"|add:show_str %}
and {{ likes|length|add:rest }} more
{% endwith %}
{% endif %}
{% endwith %}
{% endwith %}
Since I'm a very beginner with Django and Python, I'm pretty sure this approach is far worse than actually creating a custom helper! So I'm not suggesting anyone should be using this. This was just my attempt on trying to solve this with the available template helpers and without any custom stuff.
Hope this helps
Lo primero es multiplicar por -1 para convertirlo en una valor negativo y guardarlo en una variable y posterior a usar la suma
The first thing is to multiply by -1 to turn it into a negative value
and save it in a variable and then use the add
{% widthratio val2 1 -1 as result %}
{{result|add:val1}}
After search I found that I can make {% with var=value %} with filters to make the arithmetic operations "with other variables or not"
For example: I have x = 5 and y = 3 and need to add the y's value to x value, all what I need is these steps:
1- Create variable x : {% with x=5 %}
2- Create variable y : {% with y=3 %}
3- In my HTML tags, say <h1>, write that : <h1>{{ x|add:y }}</h1>
4- Close the y's with : {% endwith %}
5- Close the x's with : {% endwith %}
Hope it works with you, it worked with me.
{% with i=3 %}
{% with x=1 %}
<h1>{{i|add:x}}</h1> <!-- result is 4 -->
{% endwith %}
{% endwith %}

Numeric for loop in Django templates

How do I write a numeric for loop in a Django template? I mean something like
for i = 1 to n
I've used a simple technique that works nicely for small cases with no special tags and no additional context. Sometimes this comes in handy
{% for i in '0123456789'|make_list %}
{{ forloop.counter }}
{% endfor %}
{% with ''|center:n as range %}
{% for _ in range %}
{{ forloop.counter }}
{% endfor %}
{% endwith %}
Unfortunately, that's not supported in the Django template language. There are a couple of suggestions, but they seem a little complex. I would just put a variable in the context:
...
render_to_response('foo.html', {..., 'range': range(10), ...}, ...)
...
and in the template:
{% for i in range %}
...
{% endfor %}
My take on this issue, i think is the most pythonic. Create a my_filters.py in your apps templatetags directory.
#register.filter(name='times')
def times(number):
return range(number)
Usage in your template:
{% load my_filters %}
{% for i in 15|times %}
<li>Item</li>
{% endfor %}
You can pass a binding of
{'n' : range(n) }
to the template, then do
{% for i in n %}
...
{% endfor %}
Note that you'll get 0-based behavior (0, 1, ... n-1).
(Updated for Python3 compatibility)
Maybe like this?
{% for i in "x"|rjust:"100" %}
...
{% endfor %}
I'm just taking the popular answer a bit further and making it more robust. This lets you specify any start point, so 0 or 1 for example. It also uses python's range feature where the end is one less so it can be used directly with list lengths for example.
#register.filter(name='range')
def filter_range(start, end):
return range(start, end)
Then in your template just include the above template tag file and use the following:
{% load myapp_filters %}
{% for c in 1|range:6 %}
{{ c }}
{% endfor %}
Now you can do 1-6 instead of just 0-6 or hard coding it. Adding a step would require a template tag, this should cover more uses cases so it's a step forward.
You can pass :
{ 'n' : range(n) }
To use template :
{% for i in n %}
...
{% endfor %}
I tried very hard on this question, and I find the best answer here:
(from how to loop 7 times in the django templates)
You can even access the idx!
views.py:
context['loop_times'] = range(1, 8)
html:
{% for i in loop_times %}
<option value={{ i }}>{{ i }}</option>
{% endfor %}
You don't pass n itself, but rather range(n) [the list of integers from 0 to n-1 included], from your view to your template, and in the latter you do {% for i in therange %} (if you absolutely insist on 1-based rather than the normal 0-based index you can use forloop.counter in the loop's body;-).
Just incase anyone else comes across this question… I've created a template tag which lets you create a range(...): http://www.djangosnippets.org/snippets/1926/
Accepts the same arguments as the 'range' builtin and creates a list containing
the result of 'range'.
Syntax:
{% mkrange [start,] stop[, step] as context_name %}
For example:
{% mkrange 5 10 2 as some_range %}
{% for i in some_range %}
{{ i }}: Something I want to repeat\n
{% endfor %}
Produces:
5: Something I want to repeat
7: Something I want to repeat
9: Something I want to repeat
You should use "slice" in template, a example like this:
in views.py
contexts = {
'ALL_STORES': Store.objects.all(),
}
return render_to_response('store_list.html', contexts, RequestContext(request, processors=[custom_processor]))
in store_list.html:
<ul>
{% for store in ALL_STORES|slice:":10" %}
<li class="store_item">{{ store.name }}</li>
{% endfor %}
</ul>
This method supports all the functionality of the standard range([start,] stop[, step]) function
<app>/templatetags/range.py
from django import template
register = template.Library()
#register.filter(name='range')
def _range(_min, args=None):
_max, _step = None, None
if args:
if not isinstance(args, int):
_max, _step = map(int, args.split(','))
else:
_max = args
args = filter(None, (_min, _max, _step))
return range(*args)
Usage:
{% load range %}
<p>stop 5
{% for value in 5|range %}
{{ value }}
{% endfor %}
</p>
<p>start 5 stop 10
{% for value in 5|range:10 %}
{{ value }}
{% endfor %}
</p>
<p>start 5 stop 10 step 2
{% for value in 5|range:"10,2" %}
{{ value }}
{% endfor %}
</p>
Output
<p>stop 5
0 1 2 3 4
</p>
<p>start 5 stop 10
5 6 7 8 9
</p>
<p>start 5 stop 10 step 2
5 7 9
</p>
This essentially requires a range function. A Django feature ticket was raised (https://code.djangoproject.com/ticket/13088) for this but closed as "won't fix" with the following comment.
My impression of this idea is that it is trying to lead to programming in the template. If you have a list of options that need to be rendered, they should be computed in the view, not in the template. If that's as simple as a range of values, then so be it.
They have a good point - Templates are supposed to be very simple representations of the view. You should create the limited required data in the view and pass to the template in the context.
{% for _ in ''|center:13 %}
{{ forloop.counter }}
{% endfor %}
If the number is coming from a model, I found this to be a nice patch to the model:
def iterableQuantity(self):
return range(self.quantity)
You can use:
{% with ''|center: i as range %}
For those who are looking to simple answer, just needing to display an amount of values, let say 3 from 100 posts for example just add {% for post in posts|slice:"3" %} and loop it normally and only 3 posts will be added.
This shows 1 to 20 numbers:
{% for i in "x"|rjust:"20"|make_list %}
{{ forloop.counter }}
{% endfor %}
also this can help you:
(count_all_slider_objects come from views)
{% for i in "x"|rjust:count_all_slider_objects %}
{{ forloop.counter }}
{% endfor %}
or
{% with counter=count_all_slider_objects %}
{% if list_all_slider_objects %}
{% for slide in list_all_slider_objects %}
{{forloop.counter|add:"-1"}}
{% endfor%}
{% endif %}
{% endwith %}
You can pass range(n) instead of n in the context in views.py. This will give you an iterable list.
context['range']= range(n)
Then you can iterate in your template this way:
{% for i in range %}
<!-- your code -->
{% endfor %}
{% for i in range(10) %}
{{ i }}
{% endfor %}

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