<p>我从来没能让WTForms按照我想要的方式工作。我认为这对我的需求来说有点太重了,所以我最终使用了自己的Jinja2模板来构建表单,然后使用<code>formencode</code>库将post变量解析为dict。这对我来说足够好了。(感谢<a href="https://stackoverflow.com/questions/9058233/how-to-convert-a-multidict-to-nested-dictionary">this question</a>将我指向<code>formencode</code>库)。</p>
<p>我将粗略地介绍一下我正在使用的各种文件,然后在底部解释重要的部分:</p>
<p>应用程序py:</p>
<pre><code>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)
</code></pre>
<p>我有三个模板文件,但你当然不需要这么多。team-members.html包含与此问题相关的代码。</p>
<p>_formhelpers.html:</p>
<pre><code>{% macro render_input(id, fieldname, value) %}<input type="text" name="{{ id }}_{{ fieldname }}" value="{{ value }}" />{% endmacro %}
</code></pre>
<p>layout.html格式:</p>
<pre><code><!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>
</code></pre>
<p>组员.html:</p>
<pre><code>{% 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 %}
</code></pre>
<p>这将呈现以下HTML:</p>
<pre><code><!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>
</code></pre>
<hr/>
<p>值得一提的是app.py的<code>if request.method == 'POST':</code>部分发生了什么。<code>request.form</code>变量将是<code>ImmutableMultiDict</code>类型,打印出来时类似于这样:</p>
<pre><code>ImmutableMultiDict([('55555_share', u'0'), ('66666_share', u'1'), ('77777_share', u'1')])
</code></pre>
<p>这有点有用,但是我们仍然需要手工解析它来处理它。注意键的格式,格式为<code>id_fieldname</code>(例如<code>55555_share</code>)。这要感谢我们在formhelpers.html模板文件中放入的<code>render_input</code>宏。当我们处理post表单输入时,我们使用<code>variabledecode.variable_decode(request.form, dict_char='_')</code>,它解析表单数据并根据我们用于表单输入的<code>name</code>值的命名约定将其转换为字典。下面是它的样子:</p>
<pre><code>{
"55555": {
"share": "0"
},
"66666": {
"share": "1"
},
"77777": {
"share": "1"
}
}
</code></pre>
<p>这使得我们很容易映射回原始数据并更新它。</p>