Dynamic Forms (Formsets) in Flask / WTForms?

flask, flask-wtforms, wtforms

Solution

The problem

Your problem is that `id` is not sent back to the server - only `value` is ... and since your checkboxes don't have a `value` attribute the default value is used, which happens to be `on`.

Since you tagged this with wtforms, I'll give you an example of how you could be doing this.

Never have this issue again

The WTForms' documentation has an example class that will create a list of checkboxes for you:

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()

You would use this field in your custom form in this manner:

class FriendsForm(Form):
    potential_friends = MultiCheckboxField("Friends", coerce=int)

# ... later ...

@app.route("/add-friends", methods=["GET", "POST"])
def add_friends():
    form = FriendsForm(request.form)
    # lookup friends, for now we'll use a static list
    form.potential_friends.choices = [(1, "Sam"), (2, "Joe")]

    # We'll also need a mapping of IDs to Person instances
    # (Made up for this example - use your own ;-) )
    mapping = {
        1: Person("Sam", profile_pic="sam.jpg"),
        2: Person("Joe", profile_pic="joe.png")
    }

    if request.method == "POST":
        # Mark new friends

    return render_template("friends.html", form=form, persons=mapping)

Then, in `friends.html` you can iterate over the `form.potential_friends` field:

{% for person in form.potential_friends %}
    persons[person.data].profile_pic :: {{person.label}} :: {{person}}<br>
{% endfor %}

You can customize your HTML inside the `for` loop. My particular example should render (with a few more attributes, like `for` and `name`):

sam.jpg :: <label>Sam</label> :: <input type="checkbox" value="1">
joe.png :: <label>Joe</label> :: <input type="checkbox" value="2">

Problem

In Django you have a multiple form feature called Formsets, which you can use to create multiple forms into the same template. I am trying to achieve something similar in Flask / WTforms. ``` <form action="{{ url_for('request-accept') }}" method='post'> <table> <tbody> {% for request in requests %} <tr> <td> <div class="person-header"> <img src="{{request.profile_pic_url}}" class="img-circle profile-image"/> <p class="person-header-text">{{request.fullname()}}</p> </div> </td> <td> <input type="checkbox" id="{{request.key.urlsafe()}}" name="checkbox{{loop.index}}"> </td> </tr> {% endfor %} </tbody> </table> <input class='submit btn btn-primary' type=submit value="Connect"> </form> ``` The idea is having one form that wrapps all the checkboxes, which the user like to tick to become friends with. As it currently stands I am not really generating any form class in Flask, since I don't know how to make a dynamic FormSet, hence I create the form dynamically inside the html. The caveat is though, I don't know how to retrieve the selected `user id` via the checkbox. (I have stored it in the `id` because I didn't know better) But I can't access the id in `request.values['checkbox1']`. I can only see if its `on` or `off`. Any suggestions how to solve this please?

Original source