I have a custom Flask WTForm where I want to have a portion of that form that includes a list of button type inputs that are created based on the number of entries in a table but have been having difficulties having them show up the way I want and passing the form validation. My goal for the look of this field is to have it show up as an Inline Button Group with a Checkbox type input. Below is an example of my route method.
@bp.route('/new_channel', methods=['GET', 'POST'])
def new_channel():
# Pre-populate the NewChannelForm
newChannelForm = NewChannelForm()
newChannelForm.required_test_equipment.choices = [(equip.id, equip.name) for equip in TestEquipmentType.query.order_by('name')]
test_equipment_types = TestEquipmentType.query.all()
return render_template('new_channel.html', title='Add New Channel', form=newChannelForm,
test_equipment_types=test_equipment_types)
I have tried using a FieldList with a FormField containing a custom form with a BooleanField and managed to get the styling right but the form validation didn't work. From looking into it further, BooleanField isn't compatible with a FieldList.
My next step is to use Flask WTForm example of a MultiSelectField with a custom widget for the Field and a custom widget for the option. The default is shown below:
class MultiCheckboxField(SelectMultipleField):
"""
A multiple-select, except displays a list of checkboxes.
Iterating the field will produce subfields, allowing custom rendering of
the enclosed checkbox fields.
"""
widget = widgets.ListWidget(prefix_label=False)
option_widget = widgets.CheckboxInput()
My goal is to modify this to make a custom widget called InLineButtonGroupWidget which will use the styling for a list of in-line buttons like my picture included before. Additionally, I am looking to create a custom option_widget called CheckboxButtonInput to get the styling of each individual button where I can pass info to the field. This is what I have as the goal for the both:
InLineButtonGroupWidget:
<div class="btn-group-toggle" role="group" data-toggle="buttons"></div>
CheckboxButtonInput:
<label class="btn btn-outline-info" for="check-1">Calibrator
<input type="checkbox" id="check-1">
</label>
The documentation for how to create custom widgets is a bit over my head and doesn't explain it the best so I'm looking for some
Edit: Used Andrew Clark's suggestions and here is my final implementation:
routes.py
@bp.route('/new_channel', methods=['GET', 'POST'])
def new_channel():
class NewChannelForm(FlaskForm):
pass
test_equipment_types = TestEquipmentType.query.all()
for test_equipment_type in test_equipment_types:
# Create field(s) for each query result
setattr(NewChannelForm, f'checkbox_{test_equipment_type.name}', BooleanField(label=test_equipment_type.name, id=f'checkbox-{test_equipment_type.id}'))
newChannelForm = NewChannelForm()
if newChannelForm.validate_on_submit():
print('Form has been validated')
for test_equipment_type in test_equipment_types:
if newChannelForm.data[f'checkbox_{test_equipment_type.name}']:
channel.add_test_equipment_type(test_equipment_type)
return redirect(url_for('main.index'))
print(newChannelForm.errors.items())
return render_template('new_channel.html', title='Add New Channel', form=newChannelForm, units_dict=ENG_UNITS,
test_equipment_types=test_equipment_types)
new_channel.html
<!-- Test Equipment Selection -->
<div class="row">
<legend>Test Equipment Selection:</legend>
<div class="col-md-12">
<div class="btn-group-toggle mb-3" role="group" data-toggle="buttons">
{% for test_equipment_type in test_equipment_types %}
<label class="btn btn-outline-info" for="checkbox-{{ test_equipment_type.id }}">
{{ test_equipment_type.name }}
{{ form['checkbox_{}'.format(test_equipment_type.name)] }}
</label>
{% endfor %}
</div>
</div>
</div>