Вопрос

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.

Это было полезно?

Решение

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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top