Add fields dynamically to WTForms form
Asked Answered
K

1

8

I want to define a form class with fields based on a dict of name: label. I tried the following, which nearly worked. However, rendering the fields in a template gave AttributeError: 'UnboundField' object has no attribute '__call__'. How can I dynamically add fields to a form?

def build_form(name, record):
    class ContactForm(FlaskForm):
        name = StringField(name)
        fieldlist = {}

        for key, value in record.items():
            fieldlist[key] = StringField(key)

    @app.route('/', methods=['GET', 'POST'])
    def showform():
        form = ContactForm(request.form)

        if request.method == 'POST':
            return 'form processed'

        return render_template('cardcompare.tpl', record=record, form=form)
<form method=post>
    {{ form.name() }}
    {% for key, value in record.items() %}
        {{ form.fieldlist[key]() }}
    {% endfor %}
    <input type=submit value=Register>
</form>
Knute answered 9/5, 2017 at 2:16 Comment(0)
I
13

Use setattr to add new fields as attributes of the form class. This will cause WTForms to set up the field correctly instead of keeping the unbound field.

# form class with static fields
class MyForm(FlaskForm):
    name = StringField('static field')

record = {'field1': 'label1', 'field2': 'label2'}

# add dynamic fields
for key, value in record.items():
    setattr(MyForm, key, StringField(value))

In the template you can iterate over the fields using the attr filter.

{% for key, value in record.items() %}:
    {{ form|attr(key)() }}
{% endfor %}
Informant answered 9/5, 2017 at 15:16 Comment(10)
Thanks, is there a way though to get the dynamic fields added to a structure which can be iterated through in the template? (e.g. like fieldlist in the example)? The goal is to handle the dynamic fields differently than the static in the template.Knute
That part was deleted a litte overeagerly by an edit. I recoverd it.Informant
I've done the same by putting setattr inside __init__ of MyForm , because i need to pass attribute list to the form, it works except the first time running, do you know why?Mckamey
You need to add these fields to the class, not the instance. This means, before calling MyForm().Informant
@Informant i already added to the class but it only work since second form submit. Please take a look at my question here: #2584120Mckamey
@Mckamey Some example of your code would help. Maybe open a new question?Informant
@Informant i created my question here: #46641956Mckamey
adding attr to class is wrong, if you call this class again with other instance, the attribute will not be resetted.Mckamey
@Mckamey whether this is right or wrong depends on what behavior you are looking for ...Informant
@Mckamey - https://mcmap.net/q/83237/-how-to-copy-a-class, Chiming in a little late here but, I found creating a copy of the class (as outlined in the link) and using that to add attr to without polluting the original seemed to work for what I was trying to do.Highness

© 2022 - 2024 — McMap. All rights reserved.