Render An Editable Table Using Flask, Jinja2 Templates, Then Process The Form Data Returned
Solution 2:
I never was able to get WTForms to work quite how I wanted. I think it was a bit too heavy for my needs, so I ended up just using my own Jinja2 template to build the form and then used the formencode
library to parse the post variables into a dict. This works well enough for me. (Thanks to this question for pointing me to the formencode
library).
I'll give you a rough look at the various files I'm using and then explain the important parts at the bottom:
app.py:
from flask import Flask, render_template, request
from formencode import variabledecode
import pickledb
app = Flask(__name__)
DB = pickledb.load('data/data.db', False)
@app.route('/team-members', methods=['GET', 'POST'])
def team_members():
global DB
teammembers = DB.get('teammembers')
# teammembers looks like this, roughly:
# [{"id": 55555, "name": "Ben", "share": 0},
# {"id": 66666, "name": "Amy", "share": 1},
# {"id": 77777, "name": "Ted", "share": 1}]
if request.method == 'POST':
postvars = variabledecode.variable_decode(request.form, dict_char='_')
for k, v in postvars.iteritems():
member = [m for m in teammembers if m["id"] == int(k)][0]
member['share'] = v["share"]
DB.set('teammembers', teammembers)
DB.dump()
return render_template('team-members.html', teammembers=teammembers)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--debug', '-d', action='store_true')
parser.add_argument('--port', '-p', default=5000, type=int)
parser.add_argument('--host', default='0.0.0.0')
args = parser.parse_args()
app.run(args.host, args.port, debug=args.debug)
I have three template files, but you of course don't need this many. team-members.html has the code that's relevant to this problem.
_formhelpers.html:
{% macro render_input(id, fieldname, value) %}<input type="text" name="{{ id }}_{{ fieldname }}" value="{{ value }}" />{% endmacro %}
layout.html:
<!doctype html>
<html>
<head>
<title>Support Team Site</title>
</head>
<body>
<div class="page">
<h1>Support Team Site</h1>
{% for message in get_flashed_messages() %}
<div class=flash>{{ message }}</div>
{% endfor %}
{% block body %}{% endblock %}
</div>
</body>
</html>
team-members.html:
{% from "_formhelpers.html" import render_input %}
{% extends "layout.html" %}
{% block body %}
<form action="/team-members" method="post">
<table>
<tr>
<th>Name</th>
<th>ID</th>
<th>Inbox Share</th>
</tr>
{% for member in teammembers %}
<tr>
<td>{{member['name']}}</td>
<td>{{member['id']}}</td>
<td>{{ render_input(member['id'], 'share', member['share']) }}</td>
</tr>
{% endfor %}
</table>
<button type="submit">Send</button>
</form>
{% endblock %}
This will render the following HTML:
<!doctype html>
<html>
<head>
<title>Support Team Site</title>
</head>
<body>
<div class="page">
<h1>Support Team Site</h1>
<form action="/team-members" method="post">
<table>
<tr>
<th>Name</th>
<th>ID</th>
<th>Inbox Share</th>
</tr>
<tr>
<td>Ben</td>
<td>55555</td>
<td><input type="text" name="55555_share" value="0" /></td>
</tr>
<tr>
<td>Amy</td>
<td>66666</td>
<td><input type="text" name="66666_share" value="1" /></td>
</tr>
<tr>
<td>Ted</td>
<td>77777</td>
<td><input type="text" name="77777_share" value="1" /></td>
</tr>
</table>
<button type="submit">Send</button>
</form>
</div>
</body>
</html>
It's worth mentioning what's going on in the if request.method == 'POST':
part of app.py. The request.form
variable will be of type ImmutableMultiDict
, which would look kind of like this when printed out:
ImmutableMultiDict([('55555_share', u'0'), ('66666_share', u'1'), ('77777_share', u'1')])
This is somewhat useful, but we'd still have to parse this by hand to do anything with it. Note the format of the keys there, in the id_fieldname
format (e.g. 55555_share
). This was thanks to the render_input
macro we put in our _formhelpers.html template file. When we process the post form input, we use variabledecode.variable_decode(request.form, dict_char='_')
, which parses the form data and turns it into a dictionary based on the naming convention we used for the name
values of the form inputs. Here's what it looks like:
{
"55555": {
"share": "0"
},
"66666": {
"share": "1"
},
"77777": {
"share": "1"
}
}
This makes it easy to map back to our original data and update it.
Post a Comment for "Render An Editable Table Using Flask, Jinja2 Templates, Then Process The Form Data Returned"