Building a list in Django templates - django

With this code:
{% for o in [1,2,3] %}
<div class="{% cycle 'row1' 'row2' %}">
{% cycle 'row1' 'row2' %}
</div>
{% endfor %}
I get a TemplateSyntaxError:
Could not parse the remainder: '[1,2,3]' from '[1,2,3]'
Is there a way of building a list in a template?

We can use split method on str object :
page.html :
{% with '1 2 3' as list %}
{% for i in list.split %}
{{ i }}<br>
{% endfor %}
{% endwith %}
Results :
1
2
3

You can do it via cunning use of the make_list filter, but it's probably a bad idea:
{% for o in "123"|make_list %}
<div class="{% cycle 'row1' 'row2' %}">
{% cycle 'row1' 'row2' %}
</div>
{% endfor %}
p.s. You don't seem to be using o anywhere, so I'm not sure what you're trying to do.

I made this template tag to achieve this goal.
from django import template
register = template.Library()
# use #register.assignment_tag
# only when you're working with django version lower than 1.9
#register.simple_tag
def to_list(*args):
return args
to use it in template:
{% load your_template_tag_file %}
{% to_list 1 2 3 4 5 "yes" as my_list %}
{% for i in my_list %}
{{ i }}
{% endfor %}
Reference here:
Django simple tags

The other answers here look like the ticket (at least for what I wanted), so I'll provide an answer as to WHY you might want to do something like this (and perhaps there's a better answer for my case than what's been provided):
I came across this question looking for a way to build 3 very similar, but not identical buttons using Bootstrap. One button might look like
<div class="btn-group">
<a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#">
Modality
<span class="caret"></span>
</a>
<ul class="dropdown-menu" id="Modality">
<li>Action</li>
</ul>
</div>
where the difference between buttons is limited to the text of the button (Modality, on its own line above) and the contents of the pertaining to the button, which we'll assume is filled dynamically by JS (referencing id="Modality").
If I need to make 10 of these, copy/pasting the HTML seems dumb and tedious, especially if I want to change anything about my button after the fact (like making all of them split-drop-downs) and it goes against DRY.
So, instead, in the template I could do something like
{% with 'Modality Otherbutton Thirdbutton' as list %}
{% for i in list.split %}
<!-- copy/paste above code with Modality replaced by {{ i }} -->
{% endfor %}
{% endwith %}
Now, granted, in this particular case the buttons add functionality to some related data grid, so the button names could be dynamically filled from django model-sourced data as well, but I'm not at that stage in my design right now, and you can see where this sort of functionality is desirable to maintain DRY.

The simplest is to do
{% for x in "123" %}

drodger is correct, you can't do that in the deliberately-crippled Django template lanuage. Either pass in the list as a context variable when you invoke the template or try a template tag like expr. Then you can say {% expr [1,2,3] as my_list %} and then use my_list in your for loop.

This maybe an inspiration. Use the buildin filter add.
{{ first|add:second }}
first is [1, 2, 3] and second is [4, 5, 6], then the output will be [1, 2, 3, 4, 5, 6].
This filter will first try to coerce both values to integers.
If this fails, it'll attempt to add the values together anyway.
This will work on some data types (strings, list, etc.) and fail on others.
If it fails, the result will be an empty string.
The official specification,https://docs.djangoproject.com/zh-hans/2.0/ref/templates/builtins/#built-in-filter-reference

Related

Accessing nested list by variable in Django templates [duplicate]

I have some loop on the page and need list item depending from loop number.
When I call:
{{ mylist.1 }}
{{ mylist.2 }}
{{ mylist.3 }}
all works fine but what I really need is something like:
{% for x in somenumber|MyCustomRangeTag %}
{{ mylist.x }}
{% endfor %}
MyCustomRangeTag gives me Python range() it works and I already have x as number. So x is 1, 2, 3 etc. depending from loop number.
Is this possible and how?
This is not possible directly because Django thinks that "x" is the key to lookup in mylist - instead of the value of x. So, when x = 5, Django tries to look up mylist["x"] instead of mylist[5].
Use the following filter as workaround:
#register.filter
def lookup(d, key):
return d[key]
and use it like
{{ mylist|lookup:x }}
The slice tag in Django templates may use python's slicing code, but the syntax is unmistakably different. For instance, if you wanted to get an element of a sequence with a variable, in python you'd write something similar to the following:
>>>mylist = ["0th Element", "1th Element"]
>>>zero, one = 0, 1
>>>mylist[zero]
"0th Element"
>>>mylist[one]
"1th Element"
Using this syntax with the Django slice template tag will return a sliced list in every case, of dubious utility for getting an item of known index:
{% with "0" as zero %}
{% with "1" as one %}
{% with "2" as two %}
{{mylist|slice:zero}} {{mylist|slice:one}} {{mylist|slice:two}}
{% endwith %}
{% endwith %}
{% endwith %}
Renders to the html:
[] ["0th Element"] ["0th Element", "1th Element"]
Note the differences: you are getting the result of mylist[:x] instead of mylist[x].
Django provides enough tools to work around this. The first trick is to use explicit slices like 0:1 for your indices, and then |join:"" the resultant list into a single element. Like so:
{% with "0:1" as zero %}
{{mylist|slice:zero|join:""}}
{% endwith %}
Yields:
0th Element
This comes in particularly handy if you need to access a parent loop's index when dealing with an iterable inside a child loop:
{% for parent in parent_loop %}
{% cycle "0:1" "1:2" "2:3" as parent_loop_index silent %}
{% for child in child_loop %}
{{child|slice:parent_loop_index|join:""}}
{% endfor %}
{% endfor %}
Completed with nothing but stock parts, although I don't think Django has implemented achievements yet.
I notice that #e-satis mentioned it, but I think the built-in slice template tag deserves some love.
{{ item | slice:"2" }} #gets the third element of the list
Are you sure you can't just do:
{% for item in mylist %}
{{ item }}
{% endfor %}
With the slice filter, you can even do some customisation.
Following worked for me
{% for 1,2,3 in mylist %}
# do stuff
Just don't use brackets around 1,2,3

Adding defaults to column headers in django

I have a script which imports data in tables from other websites. The tables are between 5 and 15 columns wide and arbitrarily long.
After I've got the raw data I want the opportunity to make sure my guesses about the column headers are correct. So I want to have a at the top with a list of the 15 things a column could be called. That way I can quickly correct any poor decisions made by my automatic code.
So the auto code generates 2 arrays, the first of strings:
possible_headers = ["one", "two", "three"...]
The second of indexes into that first array
likely_headers = [2, 0, 5...]
(the columns headers would be "three" then "one" then "six")
And use them like this in my template:
{% for likely_head in likely_headers %}
<th>
<select name="colHeader">
{% for poss_head in possible_headers %}
{% if forloop.counter0 == likely_headers.forloop.parentloop.counter0 %}
<option value="Col:{{forloop.counter0}}" selected>{{poss_head}}</option>
{% else %}
<option value="Col:{{forloop.counter0}}">{{poss_head}}</option>
{% endif %}
{% endfor %}
</select>
</th>
{% endfor %}
With the idea that the likely header will be the selected/default item in the select input. Problem is the:
likely_headers.forloop.parentloop.counter0
Doesn't evaluate. forloop.parentloop.counter0 works correctly but apparently it cannot be used as an index into a list.
I'm new to django so I'm probably doing it all wrong, please be nice!
I don't see why you're using likely_headers.forloop.parentloop.counter0 when you should use forloop.parentloop.counter0 according to docs https://docs.djangoproject.com/en/dev/ref/templates/builtins/#for
You may also try solving it by encapsulating counter with {% with %} tag
{% for likely_head in likely_headers %}
{% with forloop.counter0 as parent_counter %}
{% for poss_head in possible_headers %}
{{ parent_counter }}
{% endfor %}
{% endwith %}
{% endfor %}
I haven't checked that it works for sure but I think it should.
Also you're probably trying to solve problem that should not be solved in templates. You can try using tags, processing headers in view (using library?) and just returning list of headers that should be rendered.
The list of integers I thought I had was in truth a list of strings ["1", "5", "3"] so they failed comparison to the for loop iterator. Creating a real list of ints solved the problem.

Get list item dynamically in django templates

I have some loop on the page and need list item depending from loop number.
When I call:
{{ mylist.1 }}
{{ mylist.2 }}
{{ mylist.3 }}
all works fine but what I really need is something like:
{% for x in somenumber|MyCustomRangeTag %}
{{ mylist.x }}
{% endfor %}
MyCustomRangeTag gives me Python range() it works and I already have x as number. So x is 1, 2, 3 etc. depending from loop number.
Is this possible and how?
This is not possible directly because Django thinks that "x" is the key to lookup in mylist - instead of the value of x. So, when x = 5, Django tries to look up mylist["x"] instead of mylist[5].
Use the following filter as workaround:
#register.filter
def lookup(d, key):
return d[key]
and use it like
{{ mylist|lookup:x }}
The slice tag in Django templates may use python's slicing code, but the syntax is unmistakably different. For instance, if you wanted to get an element of a sequence with a variable, in python you'd write something similar to the following:
>>>mylist = ["0th Element", "1th Element"]
>>>zero, one = 0, 1
>>>mylist[zero]
"0th Element"
>>>mylist[one]
"1th Element"
Using this syntax with the Django slice template tag will return a sliced list in every case, of dubious utility for getting an item of known index:
{% with "0" as zero %}
{% with "1" as one %}
{% with "2" as two %}
{{mylist|slice:zero}} {{mylist|slice:one}} {{mylist|slice:two}}
{% endwith %}
{% endwith %}
{% endwith %}
Renders to the html:
[] ["0th Element"] ["0th Element", "1th Element"]
Note the differences: you are getting the result of mylist[:x] instead of mylist[x].
Django provides enough tools to work around this. The first trick is to use explicit slices like 0:1 for your indices, and then |join:"" the resultant list into a single element. Like so:
{% with "0:1" as zero %}
{{mylist|slice:zero|join:""}}
{% endwith %}
Yields:
0th Element
This comes in particularly handy if you need to access a parent loop's index when dealing with an iterable inside a child loop:
{% for parent in parent_loop %}
{% cycle "0:1" "1:2" "2:3" as parent_loop_index silent %}
{% for child in child_loop %}
{{child|slice:parent_loop_index|join:""}}
{% endfor %}
{% endfor %}
Completed with nothing but stock parts, although I don't think Django has implemented achievements yet.
I notice that #e-satis mentioned it, but I think the built-in slice template tag deserves some love.
{{ item | slice:"2" }} #gets the third element of the list
Are you sure you can't just do:
{% for item in mylist %}
{{ item }}
{% endfor %}
With the slice filter, you can even do some customisation.
Following worked for me
{% for 1,2,3 in mylist %}
# do stuff
Just don't use brackets around 1,2,3

Django template for loop iterating two item

I try to implement a forloop in Django tempalte iterating two items per cycle such that
{% for c in cList%}
<ul class="ListTable">
<li>
{{ c1.name }}
</li>
<li>
{{ c2.name }}
</li>
</ul>
{% endfor %}
I know my code is not a proper way to do that but I couldn't find anyway.
I really appreciate for any suggestion
Thanks
If you can control the list structure that is cList, why don't you just make it a list of tuples of 2 elements or a list of list of 2 elements, like
#in the view
cList = [(ob1, ob2),
(ob3, ob4)]
and the in the template
{% for c1, c2 in cList %}
<ul class="ListTable">
<li>
{{ c1.name }}
</li>
<li>
{{ c2.name }}
</li>
</ul>
{% endfor %}
Also you can use the zip function to facilitate the creation of cList, or define a
function which create that kind of structure from a list of objects, like
def pack(_list):
new_list = zip(_list[::2], _list[1::2])
if len(_list) % 2:
new_list.append((_list[-1], None))
return new_list
One option is to use the the built-in template tag cycle
and do something like:
{% for c in c_list %}
{% cycle True False as row silent %}
{% if row %}
<ul class="ListTable">
{% endif %}
<li>
{{ c.name }}
</li>
{% if not row or forloop.last %}
</ul>
{% endif %}
{% endfor %}
Note: if you have odd number of element on the list the last table will have only one element with this option, sice we are checking for forloop.last
I tried to implement cyraxjoe solution which does work, but theres only one problem with it...
a = [1,2,3] will return [(1,2)] but will remove the 3.
So i was asking around in irc freenode #python for a solution and i got this:
it = iter(a); nested = [list(b) for b in itertools.izip_longest(it, it)]
print nested
[[1, 2], [3, None]]
I was also told to look up the documentation for the itertools module, and search for the "grouper" recipe. which does something similar but i havent tried it yet.
I hope this helps :)
*Credits to except and lvh from the #python channel

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