django template question (accessing a list) - python

I am writing a template for my first django website.
I am passing a list of dictionaries to the template in a variable. I also need to pass a few other lists which hold boolean flags. (Note: all lists have the same length)
The template looks something like this:
<html>
<head><title>First page</title></head><body>
{% for item in data_tables %}
<table>
<tbody>
<tr><td colspan="15">
{% if level_one_flags[forloop.counter-1] %}
<tr><td>Premier League
{% endif %}
<tr><td>Junior league
<tr><td>Member count
{% if level_two_flags[forloop.counter-1] %}
<tr><td>Ashtano League
{% endif %}
</tbody>
</table>
{% endfor %}
</body>
</html>
I am getting the following error:
Template error
In template /mytemplate.html, error at
line 7 Could not parse the remainder:
'[forloop.counter-1]' from
'level_one_flags[forloop.counter-1]'
I am, not suprised I am getting this error, since I was just trying to see if would work. So far, from the documentation, I have not found out how to obtain the items in a list by index (i.e. other than by enumeration).
Does anyone know how I may access a list by index in a template?

In short, Django doesn't do what you want.
The for loop has a number of useful properties within a loop.
forloop.counter The current iteration of the loop (1-indexed)
forloop.counter0 The current iteration of the loop (0-indexed)
forloop.revcounter The number of iterations from the end of the loop (1-indexed)
forloop.revcounter0 The number of iterations from the end of the loop (0-indexed)
forloop.first True if this is the first time through the loop
forloop.last True if this is the last time through the loop
forloop.parentloop For nested loops, this is the loop "above" the current one
You could probably use forloop.counter0 to get the zero-based indexes you want; unfortunately, the Django template language doesn't support variable array indexes (You can do {{ foo.5 }}, but you can't do {{ foo.{{bar}} }}).
What I usually do is to try and arrange the data in the view to make it easier to present in the template. As an example, for you could create an array in your view composed of dictionaries so that all you have to do is loop through the array and pull exactly what you need out of the individual dictionaries. For really complicated things, I've gone so far as to create a DataRow object that would correctly format the data for a particular row in a table.

You use the dot-operator to index the array, or, really, to do anything.
Technically, when the template system
encounters a dot, it tries the
following lookups, in this order:
* Dictionary lookup
* Attribute lookup
* Method call
* List-index lookup
I don't believe you can do math on the index. You'll have to pass in your array constructed in some other way so that you don't have to do this subtraction.

Try using "slice" to access a list by index
http://docs.djangoproject.com/en/dev/ref/templates/builtins/#slice

Perhaps a better way is to use forloop.last. Of course, this will require that you send to the template the specific level_one_flag and level_two_flag out of the level_one_flags and level_two_flags arrays, but I think this solution keeps a better logical separation between view and template:
<html>
<head><title>First page</title></head><body>
{% for item in data_tables %}
<table>
<tbody>
<tr><td colspan="15">
{% if forloop.last and level_one_flag %}
<tr><td>Premier League
{% endif %}
<tr><td>Junior league
<tr><td>Member count
{% if forloop.last and level_two_flag %}
<tr><td>Ashtano League
{% endif %}
</tbody>
</table>
{% endfor %}
</body>
</html>

Related

Dealing with json type data in a template

I am after some advice. I know what I need to do, but not sure how I can do it
I send data to a template that comes from from a database
Let's say the database has two fields
Field one (name)
Name of a person
Field two some json (the json has a structure like this, with many field:value)
{
"field1":"value1",
"field2":"value2"
}
I can output this as
Field one (name) Field two (json)
What I actually want to do though, is loop through the json, and print out the values, so like this
Name of a person field1|field2
I am a bit lost on how I can do that
I tried something like this
{% for anitem in fieldtwo %}
<div>{{ anitem }}</div>
{% endfor %}
But that just seems to print out each character
Is what I need to do achievable? I am thinking I just have the whole approach wrong
Thanks
Grant
In the end, I ended up using another dictionary and then pass that through to the template
I loop through the queryset in the view to populate the dictionary
In the template I then did this (alloweddomains is from the view)
{% for testcaseid, domains in alloweddomains.items %}
{% if listtestcase.id == testcaseid %}
{% for key, value in domains.items %}
<div><a class="sendtolinkdisabled" data-testcasename="{{ listtestcase.name }}" data-testcaseid="{{ $
{% endfor %}
{% endif %}
{% endfor %}
This basically looped through the dictionary and if there was a match to my unique ID I get the data and do something with it
I tried various ways to not need the if statement and just get the right point in the dictionary, but nothing seemed to work (so I took the if approach)
Grant

Jinja List Issue

I am getting a weird problem in Jinja, I have a list endpoints, which contains dictionary for every endpoint. In each dictionary, there is a key tags which is a list. Every item in tags is itself a dictionary where the key value gives the label of a tag. endpoint may have similar tags.
A sample abstract representation of an endpoints object can be:
[ {"tags":[{"value":"car"},{"value":"place"}]} , {"tags":[{"value":"van"},{"value":"place"}]} ]
what I want is to simple display unique tags in a div. It is simple, keeping a list of all displayed tags and upon getting a tag, checking if it is already in the list, and if not display it and add it to the list. Weirdly, it's not working.
The codes are:
{% set tagValues = [] %}
{% for endpoint in endpoints %}
{% for tag in endpoint["tags"]%}
{% set tagValue = tag["tag"]["value"] %}
{% if tagValue not in tagValues %}
{% set tagValues = tagValues + [tagValue] %}
<span >{{ tagValue }}</span></a>
{% endif %}
{% endfor %}
{% endfor %}
it is not working, for example, for the enpoints list above, I am getting the following output:
car place van place
is there any problem with the codes ?
I recommend creating a distinct list of tags in your View. e.g.
distinctTags = list(set([tag for endpoint in endpoints for tag in endpoint]))
and passing that to your template
{% for tag in distinctTags %}
<span >{{ tagValue }}</span></a>
{% endfor %}
this has the advantage of the distinct tag code being reusable and the code being less procedural.
my jinja knowledge is limited, but by adding tagValues to the output, it appears that it's reset after each iteration of the outer loop. I'd guess it's to do with scopes, but don't know.
My recommendation would be to pre-process your endpoints in regular python before passing to jinja

How to display 2 thumbnails of span6 per row in Bootstrap with Django?

I would like to display 2 thumbnails per row. It would be pretty trivial to just hard-code it using multiple rows and each row has 2 span6 div's. But how would I do this in Django using a template for-loop?
Example:
{% for image in images %}
<div class="row">
<div class="span6">*image goes here*</div>
<div class="span6">*image goes here*</div>
</div>
{% endfor %}
// repeat for all items in the list, with 2 images per row
So, in the code above span6 should be created on every loop iteration, but the row should be created only every 2 iterations.
update: I was able to span all my span6 elements inside a single row. I encountered an issue where thumbnails wouldn't align properly (empty spaces between rows). Setting all thumbnails to a uniform height fixed the problem. But Hedde's solution looks pretty good too, although that involves changing things on the Python side.
Well it could be done by css only, but if you want to use the provided grid, you could create a generator and use it on your view's queryset or directly in a template by using a tag, e.g.
def grouped(l, n):
# Yield successive n-sized chunks from l.
for i in xrange(0, len(l), n):
yield l[i:i+n]
templatetags
#register.filter
def group_by(value, arg):
return grouped(value, arg)
templates
{% for group in objects|group_by:2 %}
<div class="row">
{% for obj in group %}
<div class="span6">
foo
</div>
{% endfor %}
</div>
{% endfor %}
Here's another way to do it without adding templating logic to your Python code. You do need to make sure you have an even number of items in your list, or the cycle won't close properly.
{% for image in images %}
{% cycle "<div class='row'>" "" %}
<div class="span6">*image goes here*</div>
{% cycle "" "</div>" %}
{% endfor %}
You can make this work for more columns, just by adding additional blanks to the cycle templatetag. If anyone can think of a simple way to close the loop when there is an odd number of items in the list, I would love to hear it!

Can I evaluate expressions in a django template?

I'm trying to display table rows with alternating colors. For that, I have two css classes row1 and row2 that I'd like to assign in an alternating pattern to the rows of a table. Ideally, I'd determine if the row is odd or even based on the forloop.counter variable
This is what I'd like the template to do (invalid code, but I think it's self explaning).
{% for norma in normas %}
{% if forloop.counter %2 != 0 %}
<tr class="row1">
{% else %}
<tr class="row2">
{% endif %}
<td>yadda... yadda</td>
.
.
.
{% endfor %}
Is there a way to do this within django template system?
Use cycle - the example shows this exact purpose
Just use in your {%for%} loop :
<tr class="{% cycle 'row1' 'row2' %}>
django templete will cycle through each row. you can add as many items in the cycle.
the followin post explains how to get alternating row colors in Django.
Alternate Row Coloring in Django Template with More Than One Set of Rows

Django template iterating over list

I have a list created in Django view:
list = [ elem1, elem2, ..., elemN ]
The list is variable length: it can contain 0-6 elements. I want to iterate over the list in the template, but I would like the loop to run always 6 times, yielding None or empty string for non-existing elements.
I tried something like this:
{% for i in "0123456" %}
{{ list.i }}
{% endfor %}
but this obviously doesn't work. I know I could do this in the view, but I would like to have this in the template. Is is possible?
You can add an if statement checking if it is your 6th time through the loop.
{% for item in someList %}
{% if forloop.counter <= 6 %}
{{ item }}
{% endif %}
{% endfor %}
http://docs.djangoproject.com/en/1.3/ref/templates/builtins/#for in the docs.
Of course, if your list is very long then this is not optimal. I would also suggest processing the list in views.py and then passing it to the template. Logic should stay in the views if possible.
This gives you control over the number of loops done. To completely solve your problem you will need some addtional logic but see my note above regarding this.
Check this snippet: Template range filter

Categories