I am trying to render a dynamic form with flask-wtf (WTForms), where it would have the following dynamic data / output.
Box Name | Box Description | Box Location | Target Collection | Transfer? |
---|---|---|---|---|
Box 1 | Desc 1 | Annex | repeated select field w/ same choices | [ ] - Checkbox |
Box 2 | Desc 2 | Annex | repeated select field w/ same choices | [ ] - Checkbox |
Box 3 | Desc 3 | Annex | repeated select field w/ same choices | [ ] - Checkbox |
... | ... | ... | repeated select field w/ same choices | [ ] - Checkbox |
class MultiCheckboxField(SelectMultipleField):
widget = ListWidget(prefix_label=False)
option_widget = CheckboxInput()
class FindingAidsOptions(FlaskForm):
finding_aid_choices = SelectField("", coerce=int, validators=[DataRequired()])
class FindingAidTransferForm(FlaskForm):
target_findingaid = FieldList(FormField(FindingAidsOptions), min_entries=1, max_entries=5)
physicalobjects = MultiCheckboxField('Label',coerce=int)
submit = SubmitField('Transfer Selected Objects')
Flask Route
form = forms.FindingAidTransferForm()
form.target_findingaid.choices = findingaid_choices #This does not seem to work, as I never can iterate over the values below
form.physicalobjects.choices = physical_objects # this works, and can iterate over it
return render_template(
"atom-migrate-physical-object-listing.jinja2",
title="Accessions Phyiscal Object Migrations",
template="atom-caia-template",
atom_records=accession_record,
findingaids_physical_objects=findingaids_physical_objects,
form=form
)
# Example values for the choice data
findingaid_choices = [(26958, 'Records Choice 1'), (142451, 'Records Choice 2')]
physical_objects = [(107267, 'Box 5, TOM'), (130235, 'Box 1, A83412001377'), (139827, 'Box 2, A83411997169')]
Example template.
<form class="form" method="post" role="form">
{{ form.hidden_tag() }}
<table class="table table-striped table-sm table-responsive">
<thead>
<th>Object Name</th>
<th>Description</th>
<th>Location</th>
<th>Destination Finding Aid</th>
<th>Transfer to Findingaid?</th>
</thead>
<tbody>
{% for po in form.physicalobjects %}
<tr>
<td>{{ findingaids_physical_objects[po.data]['physical_object_name'] if findingaids_physical_objects[po.data]['physical_object_name'] is not none }}</td>
<td>{{ findingaids_physical_objects[po.data]['physical_object_desc'] if findingaids_physical_objects[po.data]['physical_object_desc'] is not none }}</td>
<td>{{ findingaids_physical_objects[po.data]['physical_object_loc'] if findingaids_physical_objects[po.data]['physical_object_loc'] is not none }}</td>
<td>Select Box with finding_aid_choices goes here</td>
<td>Check box if it should be transferred from physicalobjects</td>
</tr>
{% endfor %}
</tbody>
</table>
{% for item in form.target_findingaid %}
<pre>{{ item.id }}</pre>
{# This only seems to output target_findingaid-0, even though I have multiple. If I change the min_entries I do get an incremented value.
{% endfor %}
{{ formTextInput(form.submit) }}
</form>
I ended up figuring it out last night. I had to move some pieces around, but below is what I ended up having to do to get it working.
Form Classes:
class PhysicalObjects(Form):
box_id = HiddenField('Physical Object ID')
name = StringField('Name')
location = StringField('Location')
transfer = BooleanField('Transfer Object')
target_findingaid = SelectField("Target Findingaid", validators=[DataRequired()],coerce=int)
class FindingAidTransferForm(FlaskForm):
physicalobjects = FieldList(FormField(PhysicalObjects), min_entries=1)
submit = SubmitField('Transfer Selected Objects')
Template for loop
{% for po in form.physicalobjects.entries %}
<tr>{{po.form.box_id }}
<td>{{ po.form.name.data if po.form.name.data is not none }}</td>
<td>{{ po.form.location.data if po.form.location.data is not none }}</td>
<td>{{ po.form.target_findingaid }}</td>
<td>{{ po.form.transfer() }}</td>
</tr>
{% endfor %}
Route code
findingaid_choices = []
physical_objects = []
phys_objects_group = namedtuple("Physical_Objects", ['box_id', 'name', 'location'])
for _, rec in findingaids_physical_objects.items():
if rec['related_item_type'] == "findingaid":
findingaid_choices.append((rec['related_item_id'], rec['findingaid_title']))
else:
physical_objects.append(phys_objects_group(
rec['related_item_id'],
rec['physical_object_name'],
rec['physical_object_loc']
))
data = {'physicalobjects': physical_objects}
form = forms.FindingAidTransferForm(data=data)
for entry in form.physicalobjects.entries:
entry.target_findingaid.choices = findingaid_choices
if form.validate_on_submit():
... do stuff
else:
return render_template(
"atom-migrate-physical-object-listing.jinja2",
title="Accessions Physical Object Migrations",
template="atom-caia-template",
atom_records=accession_record,
findingaids_physical_objects=findingaids_physical_objects,
form=form
)