So, I've got a flask & wtforms app, where (for the sake of explanation) I have people, people have various attributes, but one of them is a dictionary. Keys are strings, and the value is a list which may have one or two strings, but no more.
Presently we're displaying a this dictionary on a table, however just as plain text. The html code is something like this:
{% for item in persons_attributes %}
<tr>
<th scope="row">{{ item.key }}</th>
<td>{{ item.value1 }}</td>
<td>{{ item.value2 }}</td>
</tr>
{% endfor %}
I want to make this table editable, the way I was thinking of going about it, is to have some autogenerated 'anonymous' fields, that are set by the html. The code looks like this
{% for item in persons_attributes %}
<tr>
<th scope="row"><input type="text" name="{{ item.key }}" id="{{ item.key }}" value="{{ item.key }}"></th>
<td><input type="text" name="{{ item.value1 }}" id="{{ item.value1 }}" value="{{ item.value1 }}"></td>
<td><input type="text" name="{{ item.value2 }}" id="{{ item.value2 }}" value="{{ item.value2 }}"></td>
</tr>
{% endfor %}
This works, as in, it renders them as fields with the correct values - however I can't access them elsewhere, presumably as I don't actually have any fields associated with them in wtforms? form.data where form is my form is not showing these fields - the others show up fine but not these
So yeah, I guess my question is - is this the correct way to edit values which are stored in a dictionary in flask with wtforms - and if not, what is? How do I get the fields to autopopulate if I don't know how many fields there are going to be - I've looked into fieldlist but that seems more for when you've got any number of objects associated - here we're not dealing with objects but a dictionary that is a member of the object. Any help or pointers appreciated!
You may be able to dynamically create a form using the keys contained in the dictionary and then pass the values to this form via the data
attribute.
The following example creates a form by iterating over the keys of a dictionary and adding a form field for each of those keys.
Once the form has been created, it is filled with values from the dictionary via the data
attribute and then displayed.
If all inputs are valid, the data of the form fields are queried again and assigned to the dictionary.
from flask import (
Flask,
render_template,
request
)
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired
app = Flask(__name__)
app.secret_key = 'your secret here'
class PersonForm(FlaskForm):
pass
def form_factory(keys):
class F(PersonForm):
pass
for key in keys:
field = StringField(key, [DataRequired()])
setattr(F, key, field)
return F
@app.route('/edit', methods=['GET', 'POST'])
def edit():
person = {
'key': 'key-value',
'value1': 'value1-value',
'value2': 'value2-value'
}
form = form_factory(person.keys())(request.form, data=person)
if form.validate_on_submit():
for key in person.keys():
field = getattr(form, key)
person[key] = field.data
print(person)
return render_template('edit.html', **locals())
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Edit</title>
</head>
<body>
<form method="post">
{{ form.csrf_token }}
{% for field in form -%}
{% if field.widget.input_type != 'hidden' -%}
<div>
{{ field.label() }}
{{ field() }}
{% if field.errors -%}
<ul>
{% for error in field.errors -%}
<li>{{ error }}</li>
{% endfor -%}
</ul>
{% endif -%}
</div>
{% endif -%}
{% endfor -%}
<button type="submit">Submit</button>
</form>
</body>
</html>