Loop over visible form fields

1,293 views
Skip to first unread message

JC

unread,
Sep 1, 2021, 11:40:02 AM9/1/21
to oTree help & discussion

Hi,

I followed the tutorial to loop over form fields to create a table as described here: https://otree.readthedocs.io/en/latest/forms.html?highlight=hidden#customizing-a-field-s-appearance

This worked fine, when my goal was to put all my form fields for one template in one table. However, I would like to loop over only a specific subset of the form fields as they also consist of non-iterable IntegerFields (range sliders) that are hidden. I tried it with pythons slicing {{for field in form[:-1]}}, which unfortunately did not work. After some googling I found the option in Django to acces only visible fields with form.visible_fields. Apparently, otree does not have this option. How can I instead loop over only a subset of formfields?

Many thanks for your support!

Chris @ oTree

unread,
Sep 1, 2021, 11:44:44 AM9/1/21
to oTree help & discussion
See the "complex form layout" example in otree-snippets: https://www.otreehub.com/projects/?featured=1

Just put the names of the visible formfields in a list and pass it to the template in vars_for_template. Then you can easily loop over them.

JC

unread,
Sep 2, 2021, 4:29:20 AM9/2/21
to oTree help & discussion
Thanks for the quick reply. I followed your tip and created a list in vars_for_template. Unfortunately, when I try to access the label with field.label I get an "UndefinedVariable: Cannot resolve the variable 'field.label' " error. How can I fix this?

Chris @ oTree

unread,
Sep 2, 2021, 5:34:15 AM9/2/21
to oTree help & discussion
can you show your code?

JC

unread,
Sep 2, 2021, 5:41:41 AM9/2/21
to oTree help & discussion
Does this code snippet suffice?

In _init_.py

class Task(Page):
form_model = "player"
form_fields = ["a1", "a2", "b1"]

@staticmethod
def vars_for_template(player: Player):
visible_fields = ["a1", "a2"]
hidden_fields = ["b1"]

return dict(visible_fields=visible_fields, hidden_fields=hidden_fields)

In template Task.html

<table id="likert">
<tr>
<th></th>
<th>Strongly disagree</th>
<th>Disagree</th>
<th>More or less disagree</th>
<th>Neutral</th>
<th>More or less agree</th>
<th>Agree</th>
<th>Strongly agree</th>
</tr>
<tr>
<th></th>
{{for choice in form.a1.choices}}
<th>{{choice}}</th>
{{endfor}}
</tr>
{{for field in visible_fields}}
<tr>
<td>{{ field.label }}</td>
{{for choice in field}}
<td>{{choice}}</td>
{{endfor}}
</tr>
{{endfor}}
</table>

Chris @ oTree

unread,
Sep 2, 2021, 6:02:49 AM9/2/21
to oTree help & discussion
visible_fields is a list of strings, so the variable 'field' in that loop will simply be a string like 'a1'. strings don't have a '.label' attribute.

JC

unread,
Sep 2, 2021, 6:09:53 AM9/2/21
to oTree help & discussion
I see. {{ formfield field.label }} or {{ form.field.label }} also do not work. How could I access the labels of the string and iterate through the choices? So basically I want to work with the list of strings as if it was a list of form_fields.

Chris @ oTree

unread,
Sep 2, 2021, 6:12:26 AM9/2/21
to oTree help & discussion
anyway, the technique for looping over a subset of form fields only works if you're using {{ formfield }}, which you're not. Try seeing if you can split the form into 2 pages, to make the design simpler.

JC

unread,
Sep 2, 2021, 7:44:19 AM9/2/21
to oTree help & discussion
Well using {{ formfield field }} does not allow me to access the values of the field such as '.label'. and {{ form.field.label }} does not work either:

{{for field in visible_fields}}
<tr>
<td>{{ form.field.label }}</td>
{{for choice in field}}
<td>{{choice}}</td>
{{endfor}}
</tr>
{{endfor}}

As the fields are strings "a1" and "a2", why does this not result in form.a1.label and form.a2.label? Do I need to convert field into something else for form.field.label to work?

Is there any other alternative to splitting the form into 2 pages, if I wanted to access '.label' or '.choice' in loops? What datatype is form?

Chris @ oTree

unread,
Sep 2, 2021, 12:49:00 PM9/2/21
to oTree help & discussion
On Thursday, September 2, 2021 at 4:44:19 AM UTC-7 JC wrote:
Well using {{ formfield field }} does not allow me to access the values of the field such as '.label'. and {{ form.field.label }} does not work either:

{{for field in visible_fields}}
<tr>
<td>{{ form.field.label }}</td>
{{for choice in field}}
<td>{{choice}}</td>
{{endfor}}
</tr>
{{endfor}}

As the fields are strings "a1" and "a2", why does this not result in form.a1.label and form.a2.label? Do I need to convert field into something else for form.field.label to work?


It works the same way as in Python code:

field = 'a1'
print(form.field)

This will not output form.a1; it will simply output form.field.

JC

unread,
Sep 8, 2021, 5:17:32 AM9/8/21
to oTree help & discussion
I changed this to:

getattr(form, field) with field = "a1", as I expect it to be form.a1. However this gives me the following error: TemplateSyntaxError: Unparsable argument 'form'. Arguments must be valid Python literals. (line 61, in "getattr(form,")

Since form is neither an object with form( ) nor a Python literal, what type is form?
Reply all
Reply to author
Forward
0 new messages