You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask Jinja2模板无法访问JSON对象属性问题求助

问题背景

开发Flask Web应用时,将CSV文件读取为Pandas DataFrame后在index页面渲染为表格,要求实现:用户点击按钮选中表格某行时,表格下方区域填充展示该行详情。
目前功能链路可跑通:选中行时返回的内容可完整打印输出,但无法读取返回内容内的任意属性值,试过row.first、row['first']、row.first[0]等多种属性访问写法均未生效,需要定位代码错误点,明确Jinja2模板中操作JSON对象的正确方式。

接口正常返回的单行数据结构如下:

{
  "seq": {
    "0": 1
  },
  "first": {
    "0": "Justin"
  },
  "last": {
    "0": "Powell"
  },
  "state": {
    "0": "ND"
  },
  "email": {
    "0": "idomapsok@bu.ne"
  }
}

原有代码

  • views.py
from flask import render_template, jsonify
from wsaflask import bp
import pandas as pd

data = pd.read_csv('wsaflask/input.csv')

@bp.route('/update-details/<int:id>', methods = ['POST'])
def update_details(id):
    row_details = data.loc[data['seq'] == id]
    return jsonify('', render_template('row-details.html', row = row_details.to_json()))

@bp.route('/')
def index():
    return render_template('index.html', data = data)
  • row-details.html
<div id="detail-display">
    {% if row %}
    <!-- How to print row.first (name) here -->
    <p>{{ row }}</p>
    {% endif %}
</div>
  • index.html
<body>
    <section class="container">
      <div style="max-height: 35vh; overflow-y: scroll;">
        <table class="table">
          <tr>
            {% for col in data.columns %}
            <th>{{ col }}</th>
            {% endfor %}
            <th>
              action
            </th>
          </tr>
          {% for _, row in data.iterrows() %}
          <tr>
            {% for col in data.columns %}
            <td>{{ row[col] }}</td>
            {% endfor %}
            <td>
              <button onclick="updateDetails(row_id = {{row['seq']}})" class="btn btn-link btn-sm">Select</button>
            </td>
          </tr>
          {% endfor %}
        </table>
      </div>
      <!-- Div below is replaced with row-details.html via js -->
      <div id="detail-display">
      </div>
    </section>
</body>
  • 前端Javascript代码
function updateDetails(row_id){
  var id = row_id
  $.ajax({
    url:"/update-details/" + id,
    type:"POST",
    dataType:"json",
    success: function(data){
      $('#detail-display').replaceWith(data)
    }

  });
}
核心错误点
  • 接口返回格式混乱:jsonify()的作用是生成JSON格式响应,你把render_template()生成的HTML字符串作为参数传给jsonify,最终接口返回的是一个JSON数组,不是可直接插入页面的HTML片段,前端替换DOM的逻辑从根上就不对。
  • Pandas数据转换错误:直接对筛选出的DataFrame调用to_json(),默认导出结构是「列名 -> 行索引 -> 值」的双层嵌套,就是你看到的每个字段下都多了一层"0": xxx的结构;而且你传给Jinja2的是序列化后的JSON字符串,不是Python原生字典/对象,Jinja2没法对纯字符串做属性取值,所有row.xxx、row['xxx']的写法自然全部失效。
  • 前后端数据约定矛盾:前端AJAX写了dataType: "json",说明预期接收JSON数据,但后端实际传的是混在jsonify里的HTML字符串,两边数据格式对不上。
修复方案

二选一即可,不要混写。

方案1:后端渲染HTML片段返回(改动最小,适配现有逻辑)

  1. 修改views.py:去掉无意义的jsonify包裹,筛选行时用.iloc[0]取单行Series,转成普通Python字典传给模板,避免双层嵌套结构,直接返回渲染好的HTML片段。
from flask import render_template
from wsaflask import bp
import pandas as pd

data = pd.read_csv('wsaflask/input.csv')

@bp.route('/update-details/<int:id>', methods = ['POST'])
def update_details(id):
    # 取匹配的单行数据,转成普通字典,消除内层索引嵌套
    row_details = data.loc[data['seq'] == id].iloc[0].to_dict()
    # 直接返回渲染完成的HTML片段
    return render_template('row-details.html', row = row_details)

@bp.route('/')
def index():
    return render_template('index.html', data = data)
  1. 修改row-details.html,直接用Jinja2语法读取字典属性即可:
<div id="detail-display">
    {% if row %}
    <p>序号:{{ row.seq }}</p>
    <p>名:{{ row.first }}</p>
    <p>姓:{{ row.last }}</p>
    <p>所在州:{{ row.state }}</p>
    <p>邮箱:{{ row.email }}</p>
    {% endif %}
</div>
  1. 修改前端JS,将dataType改为html,匹配后端返回的HTML片段格式:
function updateDetails(row_id){
  $.ajax({
    url:"/update-details/" + row_id,
    type:"POST",
    dataType:"html",
    success: function(data){
      $('#detail-display').replaceWith(data)
    }
  });
}

方案2:后端返回纯JSON,前端渲染(逻辑更清晰,适合后续扩展)

如果不想在后端拼接模板,可让后端直接返回JSON数据,前端拿到数据后自行拼接HTML填充:

  1. 修改views.py,直接返回JSON格式的行数据:
from flask import jsonify
from wsaflask import bp
import pandas as pd

data = pd.read_csv('wsaflask/input.csv')

@bp.route('/update-details/<int:id>', methods = ['POST'])
def update_details(id):
    row_details = data.loc[data['seq'] == id].iloc[0].to_dict()
    return jsonify(row_details)

@bp.route('/')
def index():
    return render_template('index.html', data = data)
  1. 可以删除独立的row-details.html文件,修改前端JS逻辑,拿到JSON数据后直接拼接内容填充到容器:
function updateDetails(row_id){
  $.ajax({
    url:"/update-details/" + row_id,
    type:"POST",
    dataType:"json",
    success: function(row){
      const detailHtml = `
        <div id="detail-display">
          <p>序号:${row.seq}</p>
          <p>名:${row.first}</p>
          <p>姓:${row.last}</p>
          <p>所在州:${row.state}</p>
          <p>邮箱:${row.email}</p>
        </div>
      `
      $('#detail-display').replaceWith(detailHtml)
    }
  });
}

关键提示:Jinja2是服务端模板引擎,只能操作Python原生的字典、对象类型数据,不能直接读取JSON字符串的属性。如果确实需要传JSON格式内容到模板,必须先在Python端用json.loads()把JSON字符串反序列化为字典,再传给模板渲染。

内容的提问来源于stack exchange,提问作者ishazel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 10:45:50