oTree Forum >

Forloops and variables in dynamic template - django and filters

#1 by OferG

Hello everybody,

I have been trying to access elements of lists nested in a list in order to generate a dynamic template.
However, with no success up until now. The problem is that I cannot use variables as list indices with the template tags. Concrete example:

in the __init__.py file I define:

x = random.choice([2,3])
player.participant.vars['ListOfSublists'] = []
for i in range(x):
In the template file:
{{ for i in participant.vars.ListRange }}
    {{ for j in participant.vars.SublistsRange }}
        {{ participant.vars.ListOfSublists.i }}
    {{ endfor }}
{{ endfor }}

However, the templatetags do not read 'bar' in 'foo.bar' as a variable, such that {{ participant.vars.ListOfSublists.i }} does not work and generates en error.

As a solution, I tried to implement the proposition in https://stackoverflow.com/a/29664945/19520256.
I thus created in my oTree app folder a new directory entitled 'templatetags' and created two files in it: One empty __init__.py file and another file which I named 'index.py'. I followed the instructions from the stackoverflow answer, and wrote in the index.py file:

from django import template
register = template.Library()

def index(indexable, i):
    return indexable[i]
Back to my oTree template, after installing django with PyCharm, I added {% load index %} at the top of the template and tried:

{{ for i in participant.vars.ListRange }}
    {{ for j in participant.vars.SublistsRange }}
        {{ participant.vars.ListOfSublists|index:forloop.counter0 }}
    {{ endfor }}
{{ endfor }}

yet with no success. I get the following error:
TemplateSyntaxError: Unrecognised filter name 'index:forloop.counter0' 

What can I do to overcome this difficulty?

Thank you very much,

#2 by BonnEconLab

Dear Ofer,

When oTree was still Django-based, I also used the very custom Django filter that you linked to, and it worked fine.

You don’t need anything like that in your case, though. Simply include the following code in your template:

{{ for s in participant.vars.ListOfSublists }}
    {{ for e in s }}
      {{ e }}
    {{ endfor }}
{{ endfor }}

The outer loop goes through all sublists s included in the list of sublists, while the inner loop goes through all entries e in the current sublist s.

(You can also include {{ participant.vars.ListOfSublists }} in your template for debugging to see the entire list of sublists.)



#3 by OferG

Dear Holger,

It works! Your answer is simple and smart.
I do not know why it didn't cross my mind.

Thank you very much,
With appreciation,

#4 by BonnEconLab

I’m glad I could help.

There are other situations, of course, where this custom Django “index” filter would help. Looping over several non-nested lists simultaneously would be such a situation. Imagine, for instance, that you have a list of payoffs and associated probabilities. Say, payoffs = [cu(9), cu(8), cu(7), cu(6)] and probs = [10, 20, 30, 40].

With the Django “index” filter, one could write something like

{% for p in payoffs %}
  {{ p }} with {{ probs|index:forloop.counter0 }}% probability<br>
{% endfor %}

Fortunately, also in this case doing without the filter is easy enough. One can create a joint list in the __init__.py file – for instance, via def vars_for_template(player):

        return dict(
            payoffs_and_probs = [[payoffs[i], probs[i]] for i in range(0, len(payoffs))],

One can then use

{% for pp in payoffs_and_probs %}
  {{ pp.0 }} with {{ pp.1 }}% probability<br>
{% endfor %}

in the HTML template. This is because indexing with hard-coded digits (.0, .1, etc.) works.

Write a reply

Set forum username