3

I'm struggling to figure out how to accomplish unique form field instances that are associated with another element in a table. For a contrived example, here's the following code:

from flask import Flask, render_template, request
from flask.ext.wtf import Form
from wtforms import TextField, BooleanField

app = Flask(__name__)

app.debug = True
app.config['SECRET_KEY'] = 'secret'

class Person(Form):
    fname = TextField('First Name')
    phone = TextField('Phone Number')
    email = TextField('Email Address')
    active = BooleanField('Active')

@app.route("/", methods=['GET', 'POST'])
def guest():
    names = ['Bob', 'Joe', 'Jason', 'John'] # this could be a list, or data returned from a db query, previous POST data, or whatever
    form = Person()
    if request.method == 'POST' and form.validate_on_submit():
        return str(form.data)

    return render_template('index.html', names=names, form=form)

if __name__ == "__main__":
    app.run(host='0.0.0.0')

My template:

<html>
<body>
<form action="" method="post">
<table border="1">
    <thead>
        <tr>
            <th>Name</th>
            <th>Telephone Number</th>
            <th>Email Address</th>
            <th>Active</th>
        </tr>
    </thead>
    <tbody>
        {% for n in names %}
        <tr>
            <td>{{ n }}</td>
            <td>{{ form.phone }}</td>
            <td>{{ form.email }}</td>
            <td>{{ form.active }}</td>
        </tr>
        {% endfor %}
    </tbody>
</table>

It's obvious that in the HTML, the name parameters for each input element in each table row are all identical, because the same form field is being used, so on "POST", if one enters a phone number, that phone number is now populated for all the name rows in the table. Same goes for the "Activate" checkbox. I've also tried generating a list of form instances and passing that list into the template, but it doesn't appear to be working as I expected it would. It would seem as though my options at this point are:

  1. Don't use WTForms for this (see note below)
  2. Use the WTForms FieldList + FormField (I don't think this would work either as these require named keys vs arbitrary keys so I'd run into the exact same issue.)
  3. Use HiddenFields in some fashion (Haven't even thought of how this would work).

I'm aware that I can just create the <input type> elements into the table manually via the template and assign them different names/id's based on w but this doesn't feel very idiomatic, plus I'd have to code in the validation on my own.

1 Answer 1

3

You normally would pass in a multi-dict object containing the form data as the first argument to the form:

form = UpdateWidgetForm(request.form)

this does require that the POST data matches the widgets in the form; each widget will inspect that multi-dict and pull out what it needs to render with some pre-populated data.

Alternatively, you can pass in values as keyword arguments, but that does require you've validated the input yourself, converting any values to the right types:

number = request.form.get('widget_number')
try:
    number = int(number)
except ValueError:
    abort(404)
policy = request.form.get('widget_policy')
if policy not in ('foo', 'bar'):
    abort(404)

form = UpdateWidgetForm(widget_number=number, widget_policy=policy)

See How Forms get data in the documentation.

If you need to generate multiple forms within a page, each unique, from the same form object, you need to give them each a unique prefix with the Form(prefix=..) argument.

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks for your response Martijn. I've spent a good bit of today poking at this (re: passing in the MultiDict to the form) and I'm still a little stuck. I'd prefer to not start an unnecessarily long comments thread, so would you be open to a chat or e-mail thread about this? Thanks.
I've gotten a little farther in understanding (I think). I ultimately don't think this (what I'm trying to accomplish) is even possible with wtforms. The MultiDict operates under having multiple values with the same key - the key in this instance corresponding to the field name (and thus the name parameter in the input element in the generated HTML). What I want is the complete opposite - generate multiple fields from the same Form class each with a unique name based on some other input.
@JohnJensen: then use the prefix argument to Form(): UpdateWidgetForm(prefix='unique_prefix') to differtiate the forms.
That would make sense, but this is ultimately a single form being rendered with multiple fields. I'll edit my post to create a more contrived example that may make it more clear what I'm trying to accomplish.
You can still use multiple form objects to handle those fields though.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.