Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic Forms (Formsets) in Flask / WTForms?

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?

like image 241
Houman Avatar asked Aug 12 '13 13:08

Houman


1 Answers

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">
like image 71
Sean Vieira Avatar answered Dec 10 '22 12:12

Sean Vieira