python - 烧瓶将 request.form 值传递给 url_for
问题描述
我有一个 Flask 模板,它显示一个页面,其中包含一个dropdown
列表owners
,一个table
包含所有者的输赢记录,以及一个在赛季记录和记录radio
之间切换的页面。regular
playoff
所需的工作流程是:
- 如果通过导航栏导航到页面,则默认为
/matchup-history/regular
. (这有效) - 否则,无论何时
radio
切换,它都应该相应地路由。(这不起作用)
matchup-history.html
{%- extends "base.html" -%}
{% block nav_matchups %}active{% endblock %}
{%- block content -%}
<form action="{{ url_for('show_matchup_history', matchup_type=request.form['matchup_type']) }}" method="post">
<label>
<select name="owner_id" onchange="this.form.submit()">
{%- for o in owners %}
{%- if request.form['owner_id'] == o['owner_id']|string() %}
<option value="{{ o['owner_id'] }}" selected>{{o['first_name'] + " " + o['last_name'] }}</option>
{%- else %}
<option value="{{ o['owner_id'] }}">{{o['first_name'] + " " + o['last_name'] }}</option>
{%- endif %}
{%- endfor %}
</select>
</label>
{% block matchup_type_radio %}{% endblock %}
</form>
{%- if records|length > 0 %}
<div class="stats-table">
<table>
<tr>
{%- for th in table_headers %}
<th>{{ th }}</th>
{%- endfor %}
</tr>
{%- for r in records %}
<tr>
{%- for cn in column_names %}
<td>{{ r[cn] }}</td>
{%- endfor %}
</tr>
{%- endfor %}
</table>
</div>
{%- endif %}
{% endblock -%}
比赛历史/regular.html
{%- extends "matchup-history.html" -%}
{% block matchup_type_radio %}
<label><input type="radio" name="matchup_type" value="regular" onclick="this.form.submit()" checked>Regular Season</label>
<label><input type="radio" name="matchup_type" value="playoffs" onclick="this.form.submit()">Playoffs</label>
{% endblock %}
比赛历史/季后赛.html
{%- extends "matchup-history.html" -%}
{% block matchup_type_radio %}
<label><input type="radio" name="matchup_type" value="regular" onclick="this.form.submit()">Regular Season</label>
<label><input type="radio" name="matchup_type" value="playoffs" onclick="this.form.submit()" checked>Playoffs</label>
{% endblock %}
应用程序.py
@app.route('/matchup-history/<string:matchup_type>', methods=['GET', 'POST'])
def show_matchup_history(matchup_type):
table_headers = ["Opponent", "Wins", "Losses"]
column_names = ["opponent_owner_name", "wins", "losses"]
owners = queries.get_owners()
if request.method == 'POST':
owner_id = request.form['owner_id']
else:
owner_id = owners[0]['owner_id']
if matchup_type == REGULAR_SEASON:
records = queries.get_matchup_history_regular(owner_id)
else:
records = queries.get_matchup_history_playoffs(owner_id)
return render_template("matchup-history/{matchup_type}.html".format(matchup_type=matchup_type),
title='Matchup History', table_headers=table_headers, column_names=column_names,
owners=owners, records=records)
单击时页面正确加载/matchup-history/regular
,但只要切换单选按钮就会失败:
127.0.0.1 - - [20/Sep/2018 08:32:53] "GET /matchup-history/regular HTTP/1.1" 200 -
127.0.0.1 - - [20/Sep/2018 08:32:56] "POST /matchup-history/ HTTP/1.1" 404 -
渲染的时候好像request.form['matchup_type']
是空matchup-history.html
的,所以提交表单不会有想要的效果。我怎样才能重构路由url_for
到不同的matchup_type
?
编辑:根据@Joost的建议,我重新考虑了设计。
matchup-history.html
{%- extends "base.html" -%}
{% block nav_matchups %}active{% endblock %}
{%- block content -%}
<form action="{{ url_for('show_matchup_history') }}" method="get">
<label>
<select name="owner_id" onchange="this.form.submit()">
{%- for o in owners %}
<option value="{{ o['owner_id'] }}" {%- if o['owner_id'] == selected_owner %} selected {% endif %}>{{o['first_name'] + " " + o['last_name'] }}</option>
{%- endfor %}
</select>
</label>
<label><input type="radio" name="matchup_type" value="regular" onclick="this.form.submit()" {%- if matchup_type == "regular" %} checked {% endif %}>Regular Season</label>
<label><input type="radio" name="matchup_type" value="playoffs" onclick="this.form.submit()"{%- if matchup_type == "playoffs" %} checked {% endif %}>Playoffs</label>
</form>
{%- if records|length > 0 %}
<div class="stats-table">
<table>
<tr>
{%- for th in table_headers %}
<th>{{ th }}</th>
{%- endfor %}
</tr>
{%- for r in records %}
<tr>
{%- for cn in column_names %}
<td>{{ r[cn] }}</td>
{%- endfor %}
</tr>
{%- endfor %}
</table>
</div>
{%- endif %}
{% endblock -%}
base.html
...
<a href="{{ url_for('show_matchup_history') }}" class="{% block nav_matchups %}{% endblock %}">Matchups</a>
...
应用程序.py
@app.route('/matchup-history', methods=['GET'])
def show_matchup_history():
table_headers = ["Opponent", "Wins", "Losses"]
column_names = ["opponent_owner_name", "wins", "losses"]
matchup_type = request.args.get('matchup_type', default="regular")
owner_id = request.args.get('owner_id', type=int)
owners = queries.get_owners()
if not owner_id:
owner_id = owners[0]['owner_id']
if matchup_type == REGULAR_SEASON:
records = queries.get_matchup_history_regular(owner_id)
else:
records = queries.get_matchup_history_playoffs(owner_id)
return render_template("matchup-history.html".format(matchup_type=matchup_type),
title='Matchup History', table_headers=table_headers, column_names=column_names,
matchup_type=matchup_type, selected_owner=owner_id, owners=owners, records=records)
现在的流程是:
- 从导航栏单击
Matchups
将路由到/matchup-history
并默认显示常规赛季对决 - 点击
Playoffs
收音机将路由到/matchup-history?matchup_type=playoffs&owner_id=12345
- 点击
Regular
收音机将路由到/matchup-history?matchup_type=regular&owner_id=12345
- 单击
dropdown
遗嘱路线中的其他所有者/matchup-history?matchup_type=regular&owner_id=98765
解决方案
所以现在你正试图request.form
在获取请求中访问。但是,form
get 请求中的 总是为空的,因为这是 get 请求的本质。因此,只有当您通过@app.route('/matchup-history/<string:matchup_type>'
发布请求访问路由时,它才能以正确的方式重定向。
这个工作的小应用程序很好地显示了它:
from flask import Flask, render_template_string, request
app = Flask(__name__)
TEMPLATE_STRING = """
<form action="{{ url_for('index') }}" method="post">
{{request.form['matchup_type']}}<br><br>
<label><input type="radio" name="matchup_type" value="regular" onclick="this.form.submit()" checked>Regular Season</label>
<label><input type="radio" name="matchup_type" value="playoffs" onclick="this.form.submit()">Playoffs</label>
</form>
"""
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
return render_template_string(TEMPLATE_STRING)
else:
return render_template_string(TEMPLATE_STRING)
第一次打开页面时,您只会看到单选按钮。但是,一旦您单击单选按钮,它就会发布表单,因此您现在将在页面顶部看到选定的值。如果再次单击,则会再次发布表单,等等。
那么应该如何解决呢?我认为没有必要用这个表单做一个 POST 请求,因为你没有更新任何数据,你只是在查询。
from flask import Flask, render_template_string, request
app = Flask(__name__)
TEMPLATE_STRING = """
<form action="{{ url_for('history') }}" method="get">
<select name="owner_id">
{% for owner in owners %}
<option {% if owner['id'] == selected_owner_id %} selected {% endif %}value="{{owner['id']}}">{{owner['name']}}</option>
{% endfor %}
</select>
<label><input type="radio" name="matchup_type" value="regular" {%if selected_matchup_type == 'regular'%}checked{%endif%} onclick="this.form.submit()">Regular Season</label>
<label><input type="radio" name="matchup_type" value="playoffs" {%if selected_matchup_type == 'playoffs'%}checked{%endif%} onclick="this.form.submit()" >Playoffs</label>
<br>Queried data goes here
</form>
"""
owners = [{'id': 1, 'name': 'bob'}, {'id': 2, 'name': 'gary'}, {'id': 3, 'name': 'tom'}]
matchup_types = 'regular', 'playoffs'
@app.route('/history', methods=['GET'])
def history():
owner_id = request.args.get('owner_id', None, type=int)
if owner_id not in [owner['id'] for owner in owners]:
owner_id = owners[0]['id']
matchup_type = request.args.get('matchup_type', None)
if matchup_type not in matchup_types:
matchup_type = matchup_types[0]
# now you know the owner_id and the matchup type, and know that both are valid, do some query to get table data
return render_template_string(TEMPLATE_STRING, owners=owners,
selected_owner_id=owner_id,
selected_matchup_type=matchup_type,
matchup_types=matchup_types)
我认为这就是你所需要的。表单永远不会发布,始终作为获取请求 ( <form action="{{ url_for('history') }}" method="get">
)。如果值丢失或无效,我们默认返回某个所有者/匹配类型。检查的值会被记住,并用于呈现模板。
这会将所有烧瓶逻辑放入模板中@app.route
,并将所有 jinja 逻辑放入模板中。
一些一般性说明:
我认为访问request
jinja 并不可取,因为 jinja 以不同的方式处理错误/缺失值,如果它们是与您的请求相关的逻辑的结果,那么很难猜测发生了什么。所以在python端处理传入的请求。
而不是根据所选值包装 2 个无线电块,只需使用一个块并在选项中检查是否需要一个。<option {% if some_value == some_other_value %} checked {% endif%}>blabla</option>
.
做更多的输入验证!在您的第一个示例中,您的模板名称由一些用户输入的值(匹配类型)决定。但是如果用户发布了一个不存在的值呢?你得到错误。
如果两个模板之间的唯一区别是选择了哪个单选按钮,则不需要两个模板。查看更新版本如何在一个模板中处理它。
推荐阅读
- html - 我应该如何将变量从 app.js 传递给我的 HTML 文件?
- twig - 无法将脚本部分加载到 html 标头中
- ssis - 我可以在一个带参数的 sql 任务中拥有多个更新语句吗?
- vue.js - Vue项目中的views和components文件夹有什么区别?
- shell - 反转相邻数字对
- jspdf - html2canvas 不适用于大型 HTML 内容
- python-3.x - 创建具有两个张量 tensorFlow 的字典
- sum - 如何对满足特定条件的两列求和
- entity-framework-core - 在实体框架核心 2.1 上搭建无钥匙模型
- amazon-web-services - 如何使用 AWS elb 创建可扩展的 Websocket 应用程序?