Python 使用Flask、Jinja2模板呈现可编辑表,然后处理返回的表单数据
我正在使用Flask和Jinja2,我需要创建一个具有多行的可编辑表 这是表格的外观: 下面是HTML:Python 使用Flask、Jinja2模板呈现可编辑表,然后处理返回的表单数据,python,flask,jinja2,wtforms,Python,Flask,Jinja2,Wtforms,我正在使用Flask和Jinja2,我需要创建一个具有多行的可编辑表 这是表格的外观: 下面是HTML: <form action="/support/team-members-update" method="post"> <table> <tbody><tr> <th>Name</th> <th>Id</th> <th>Inbox Share</th&
<form action="/support/team-members-update" method="post">
<table>
<tbody><tr>
<th>Name</th>
<th>Id</th>
<th>Inbox Share</th>
</tr>
<tr>
<td>Ben</td><td>55555</td><td><input type="text" name="share_55555" value="0"></td></tr> <tr>
<td>Steve</td><td>66666</td><td><input type="text" name="share_66666" value="1"></td></tr> <tr>
<td>Harry</td><td>77777</td><td><input type="text" name="share_77777" value="1"></td></tr> <tr>
<td>Sally</td><td>88888</td><td><input type="text" name="share_88888" value="1"></td></tr></tbody></table>
<button type="submit">Send</button>
</form>
名称
身份证件
收件箱共享
本55555
史蒂文66666
哈里77777
莎莉88888
发送
我当前的实现是在Lua中,我正在硬编码一堆字符串,并手动将post数据连接到本地Lua类型(很有趣!)。如果有必要,我也可以用Python手工处理表单数据,但我想可能有更好的解决方案
我对WTForms进行了一些探索,但没有太多的运气让它正常工作 我确实找到了,但这似乎处理的是同一字段的列表,而不是具有相同确切字段的多行
我也发现了,但文档很少,我不知道如何实现它,以知道这是否能满足我的要求。FieldList将起作用,您需要列出一个列表。按如下方式指定表单字段:
class MemberForm(Form):
name = StringField('name')
member_id = StringField('member_id')
inbox_share = IntegerField('inbox_share')
# etc.
class TeamForm(Form):
title = StringField('title')
teammembers = FieldList(FormField(MemberForm))
然后,您可以使用如下视图函数从数据库中创建表单:
@app.route('/support/team-members-update', methods=['GET','POST'])
def update_team_members():
teamform = TeamForm()
teamform.title.data = "My Team" # change the field's data
for member in DB.get('teammembers') # some database function to get a list of team members
member_form = MemberForm()
member_form.name = member.name # These fields don't use 'data'
member_form.member_id = member.id
member_form.inbox_share = member.share
teamform.teammembers.append_entry(member_form)
return render_template('edit-team.html', teamform = teamform)
然后在模板中,您可以在创建表行时迭代teammembers
中的每个项目:
<html>
<head>
<title>Edit Team Members</title>
</head>
<body>
<h1>Edit Team</h1>
<div>
<form action="" method="post" name="teamform">
{{ teamform.hidden_tag() }}
Team Title: {{ teamform.title }}<br>
<div>
<table>
<tr>
<th> Name </th>
<th> ID </th>
<th> Inbox Share </th>
</tr>
{% for member in teamform.teammembers %}
<tr>
<td>{{ member.name }}</td>
<td>{{ member.member_id }}</td>
<td>{{ member.inbox_share }}</td>
</tr>
{% endfor %}
</table>
</div>
<p><input type="submit" name="edit" value="Send"></p>
</form>
</div>
</body>
</html>
编辑团队成员
编辑团队
{{teamform.hidden_tag()}}
团队标题:{{teamform.Title}}
名称
身份证件
收件箱共享
{teamform.teammembers%中的成员为%s}
{{member.name}
{{member.member_id}
{{member.inbox_share}
{%endfor%}
我一直无法让WTForms按我所希望的方式工作。我认为它对我的需求来说有点太重了,所以我最终只使用我自己的Jinja2模板来构建表单,然后使用formencode
库将post变量解析为dict。这对我来说已经足够好了。(感谢您为我指出formencode
库)
我将大致介绍我使用的各种文件,然后在底部解释重要部分:
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)
我有三个模板文件,但你当然不需要这么多。team-members.html包含与此问题相关的代码
_formhelpers.html:
{% macro render_input(id, fieldname, value) %}<input type="text" name="{{ id }}_{{ fieldname }}" value="{{ value }}" />{% endmacro %}
这有点有用,但是我们仍然需要手工解析它来处理它。在id\u fieldname
格式(例如55555\u share
)中,注意此处键的格式。这要归功于我们在_formhelpers.html模板文件中放入的render\u input
宏。当我们处理post表单输入时,我们使用variabledecode.variable\u decode(request.form,dict\u char=''''.''''.
),它解析表单数据,并根据我们用于表单输入的名称
值的命名约定将其转换为字典。下面是它的样子:
{
"55555": {
"share": "0"
},
"66666": {
"share": "1"
},
"77777": {
"share": "1"
}
}
这使得映射回原始数据并进行更新变得很容易。这看起来很有希望,但我无法弄清楚如何将其连接起来并设置适当的模板。最后,我使用Jinja2模板手工制作了自己的表单,然后使用
formencode
库中的variabledecode
解析post变量。我将在我的解决方案中单独发布一个答案。我很高兴你找到了一个适合你的解决方案。如果你想看我的代码,我在一个单独的问题中发布了我的代码(后来解决了一个不同的问题)。在其中,我显示了一个学生表,其中包含学生ID和姓名的输入字段,作为另一个班级表格的一部分。嗨,这很好,我一直在寻找,现在我得到了表ok,我如何读取已经改变的值?我一直在寻找关于这个的文档,我很惊讶没有更好的文档来描述这个常见的用例。有没有关于使用熊猫喂食的想法。唯一复杂的可能是将表格的一列作为表单的一部分进行编辑(仅使用pandas.to_html。这是一个很好的示例!!!我刚刚开始使用flask,这似乎是一个很好的开始。我相信我设置正确。它会正确运行,直到我加载页面,然后出现错误:“TypeError:列表索引必须是整数,而不是str”我只是在设置JSON数据时失败了吗?这是一行:teammembers=DB.get('teammembers')。为什么它使用'teammembers'而不是索引?
{% 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 %}
<!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>
ImmutableMultiDict([('55555_share', u'0'), ('66666_share', u'1'), ('77777_share', u'1')])
{
"55555": {
"share": "0"
},
"66666": {
"share": "1"
},
"77777": {
"share": "1"
}
}