python - Flask 将 request.form 值传递给 url_for

标签 python html forms flask

我有一个 Flask 模板,它显示一个页面,其中包含所有者下拉列表,以及包含所有者输赢记录的表格,以及一个 radio ,用于在常规赛季记录和季后赛记录之间切换。

所需的工作流程是:

  1. 如果通过导航栏导航到该页面,则应默认为 /matchup-history/regular。 (这有效)
  2. 否则,每当 radio 切换时,它都应该进行相应的路由。 (这不起作用)

ma​​tchup-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 -%}

ma​​tchup-history/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 %}

ma​​tchup-history/playoffs.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 %}

app.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 -

渲染 matchup-history.html 时,request.form['matchup_type'] 似乎为空,因此提交表单不会达到预期效果。如何重构以将 url_for 路由到不同的 matchup_type

编辑:根据@Joost根据建议,我重新考虑了设计。

ma​​tchup-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>
...

app.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)

现在的流程是:

  1. 点击导航栏中的比赛将转到/matchup-history并默认显示常规赛季比赛
  2. 点击季后赛广播将路由至/matchup-history?matchup_type=playoffs&owner_id=12345
  3. 点击常规广播将路由至/matchup-history?matchup_type=regular&owner_id=12345
  4. 点击下拉列表中的其他所有者将路由至/matchup-history?matchup_type=regular&owner_id=98765

最佳答案

所以现在您正在尝试访问 request.form在获取请求中。然而,form在 get 请求中将始终为空,因为这是 get 请求的本质。所以只有当你访问路由@app.route('/matchup-history/<string:matchup_type>'时通过post请求,它能够以正确的方式重定向吗?

这个工作迷你应用程序显示得很好:

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)

我想这就是你所需要的。该表单永远不会发布,始终作为 get 请求 ( <form action="{{ url_for('history') }}" method="get"> ) 放置。如果值丢失或无效,我们将默认返回某个所有者/matchup_type。所检查的值将被记住,并用于渲染模板。

这会将所有 flask 逻辑放入 @app.route 中,以及模板中的所有 jinja 逻辑。

一些一般性评论:

我认为访问request在 jinja 中并不可取,因为 jinja 处理错误/缺失值的方式不同,如果它们是与您的请求相关的逻辑结果,则很难猜测发生了什么。因此在 python 端处理传入的请求。

不必根据所选值包装 2 个单选 block ,只需使用一个 block 并在选项中检查是否符合您的需要。 <option {% if some_value == some_other_value %} checked {% endif%}>blabla</option> .

进行更多的输入验证!在第一个示例中,模板名称由某些用户输入的值(匹配类型)决定。但是如果用户发布了不存在的值怎么办?您会收到错误。

如果两个模板之间的唯一区别是选择了哪个单选按钮,则不需要两个模板。请参阅更新版本如何在一个模板中处理它。

关于python - Flask 将 request.form 值传递给 url_for,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52429033/

相关文章:

html - 在表单中,为什么标签的 "for"属性与字段的 "name"属性不匹配?

python - 使用 asyncio.create_subprocess_exec 设置最大并发数

python - 将 pandas Dataframe 的行转换为字符串列表

javascript - Ajax Get请求找不到JSON文件

来自url查询字符串中变量的javascript表单字段填充

javascript - 使用 Javascript 框架重构巨大的表单

不分配变量的 Python 列表

python - 为什么我的正则表达式模式允许数字?

html - 将图像固定在顶部,但两侧有自动边距

javascript - 动态 html 表单