Python 创建自定义表单小部件

Python 创建自定义表单小部件,python,forms,flask,widget,wtforms,Python,Forms,Flask,Widget,Wtforms,我有一个定制的FlaskWTForm,其中我希望表单的一部分包含一个按钮类型输入列表,该列表是根据表中的条目数创建的,但一直很难让它们以我想要的方式显示并通过表单验证。我的目标是让这个领域的外观显示为一个整体。下面是我的路线方法的一个示例 @bp.route('/new_channel',methods=['GET',POST'] def新建_通道(): #预填充NewChannelForm newChannelForm=newChannelForm() newChannelForm.requi

我有一个定制的Flask
WTForm
,其中我希望表单的一部分包含一个按钮类型输入列表,该列表是根据表中的条目数创建的,但一直很难让它们以我想要的方式显示并通过表单验证。我的目标是让这个领域的外观显示为一个整体。下面是我的路线方法的一个示例

@bp.route('/new_channel',methods=['GET',POST']
def新建_通道():
#预填充NewChannelForm
newChannelForm=newChannelForm()
newChannelForm.required_test_equipment.choices=[(equipment.id,equipment.name)用于TestEquipmentType.query.order_by('name')]
test\u device\u types=TestEquipmentType.query.all()
返回渲染模板('new\u channel.html',title='addnewchannel',form=newChannelForm,
测试设备类型=测试设备类型)
我曾尝试将
FieldList
FormField
一起使用,其中包含一个带有
BooleanField
的自定义表单,并成功地获得了正确的样式,但表单验证不起作用。进一步研究,
布尔字段
字段列表
不兼容

我的下一步是使用Flask
WTForm
示例
MultiSelectField
,其中字段有一个自定义小部件,选项有一个自定义小部件。默认设置如下所示:

class MultiCheckBox字段(选择MultipleField):
"""
“多选”对话框将显示复选框列表。
迭代该字段将生成子字段,允许自定义渲染
包含的复选框显示在字段中。
"""
widget=widgets.ListWidget(前缀\标签=False)
option_widget=widgets.CheckboxInput()
我的目标是修改它,使之成为一个名为
InLineButtonGroupWidget
的自定义小部件,它将使用样式来创建一个内嵌按钮列表,就像前面包含的图片一样。此外,我希望创建一个名为
CheckboxButtonInput
的自定义
option\u小部件
,以获得每个按钮的样式,我可以在其中向字段传递信息。这是我对这两个项目的目标:

InLineButtonGroupWidget


复选框按钮输入

校准器
关于如何创建自定义小部件的文档有点让我不知所措,没有最好地解释它,所以我正在寻找一些

编辑: 使用了Andrew Clark的建议,以下是我的最终实现:

routes.py

@bp.route('/new_channel',methods=['GET',POST']
def新建_通道():
类别新通道形式(烧瓶形式):
通过
test\u device\u types=TestEquipmentType.query.all()
对于测试设备类型中的测试设备类型:
#为每个查询结果创建字段
setattr(NewChannelForm,f'checkbox{test\u device\u type.name}',BooleanField(label=test\u device\u type.name,id=f'checkbox-{test\u device\u type.id}'))
newChannelForm=newChannelForm()
如果newChannelForm.validate_on_submit():
打印('表单已验证')
对于测试设备类型中的测试设备类型:
如果newChannelForm.data[f'checkbox{test\u device\u type.name}']:
通道。添加测试设备类型(测试设备类型)
返回重定向(url_for('main.index'))
打印(newChannelForm.errors.items())
返回渲染模板('new\u channel.html',title='Add new channel',form=newChannelForm,units\u dict=ENG\u units,
测试设备类型=测试设备类型)
new_channel.html


试验设备选择:
{测试设备类型中测试设备类型的百分比%}
{{test_device_type.name}
{{form['checkbox{}.格式(测试设备类型.名称)]}
{%endfor%}

我通常通过以下方式处理表单构建:

def buildNewChannelForm():
    class NewChannelForm(FlaskForm):
        # put any non dynamic fields here
        pass

    test_equipment_types = TestEquipmentType.query.all()
    for test_equipment_object in test_equipment_types:
        # create field(s) for each query result
        setattr(NewChannelForm, f'field_name_{test_equipment_object.id}', SelectField(label='label name', choices=[(equip.id, equip.name) for equip in TestEquipmentType.query.order_by('name')]))

    return NewChannelForm()
编辑1: 我不确定是否有更好的方法,但我通常会这样做来处理数据提交 然后可以使用变量列表呈现表单,只需包含在render_template语句中即可 然后在route中提交表单时,它只是一本字典。所以你可以这样做
请详细说明这一部分->“我有一部分表单,其中包括一些复选框类型的输入,这些输入是根据表中的条目数(~5-10)创建的。”“您希望根据给定值在屏幕截图中显示相同的3个按钮,5到10次吗?或者有许多不同的按钮,如果值==5,您将始终包含这5个按钮,如果值为6,您将提供相同的5个按钮加上一个额外的按钮?我编辑了我的帖子,在我的routes方法中包含一些代码。我的意思是,我查询一个表,该表返回许多项,这些项在填充时应该只有大约5-10项。我将使用每个TesteDeviceType的id和名称来填充标签和输入字段的id,以及标签文本的名称。动态fieldname实现只是一个建议。我建议您做一些事情使其与众不同。因此,这会在表单中添加新字段,然后您可以在模板文件中指定它们的位置/格式?另外,在新字段通过form.validate\u on\u submit后,您建议如何从新字段中提取数据?
def buildNewChannelForm():
    new_channel_form_variable_list = []
    class NewChannelForm(FlaskForm):
        # put any non dynamic fields here
        pass

    test_equipment_types = TestEquipmentType.query.all()
    for test_equipment_object in test_equipment_types:
        # create field(s) for each query result
        setattr(NewChannelForm, f'field_name_{test_equipment_object.id}', SelectField(label='label name', choices=[(equip.id, equip.name) for equip in TestEquipmentType.query.order_by('name')]))

        # append variable name
        new_channel_form_variable_list.append(f'field_name_{test_equipment_object.id}')

    return NewChannelForm(), new_channel_form_variable_list
{% for variable_name in new_channel_form_variable_list %}
    {{ form[variable_name] }}
{% endfor %}

result_dictionary = form.data

# either loop through your variable list or handle each one individually
for variable_name in new_channel_form_variable_list:
    print(f'variable name: {variable_name}, value: {result_dictionary[variable_name]}')