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

如何在表格列中显示API响应值且避免数据被覆盖?

问题解决:Django+DataTables中异步填充每行API数据的正确方案

问题背景

使用Django、django-filter、django-crispy-forms和DataTables开发网站,已实现从数据库拉取数据展示在表格中。现在需要调用外部API获取每个玩家的实时状态并填充到对应列,但遇到问题:

  • 异步Ajax模式下,仅表格第一行能正确填充数据
  • 同步模式下,后续行的API响应会覆盖前一行的数据
  • 控制台显示所有API调用均已执行,但数据无法对应到各自行

问题根源

核心问题是所有行的状态容器使用了同一个ID tdtargetstatus:

  • jQuery通过$("#tdtargetstatus")选择元素时,只会匹配DOM中第一个拥有该ID的元素,所有Ajax请求的结果都会往第一行的容器里填充
  • 循环中每行都嵌入独立的$(document).ready脚本,导致重复绑定,代码冗余且逻辑混乱

最佳实现方案

步骤1:给每个状态容器设置唯一标识

在模板循环中,用玩家的tcpuserid作为容器ID的后缀,确保每个行的状态容器唯一。

步骤2:统一处理Ajax请求

将所有Ajax请求逻辑放到页面底部的$(document).ready中,遍历表格的每一行,获取对应玩家ID后发起请求,将结果填充到对应行的容器里。

步骤3:API密钥安全优化(重要)

当前代码直接在前端暴露API密钥,存在被盗用的风险,建议通过Django后端代理API请求:

  • 在Django中编写视图函数,接收玩家ID,后端调用外部API并返回结果
  • 前端Ajax请求自己的Django后端接口,避免密钥泄露

修改后的完整代码片段

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href= "{%  static 'css/style.css' %}">
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/dt/dt-1.12.1/datatables.min.css"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/v/dt/dt-1.12.1/datatables.min.js"></script>
<title>TornEQCenter Targets</title>
</head>
<body>
    <nav class="navbar">
        <div class="menubar">
            <ul class="list">
                {% if user.is_authenticated %}
                <li class="list-items"><a href="home">TornEQCenter</a></li>
                <li class="list-items"><a href="targets">Targets</a></li>
                <div class="collapse navbar-collapse" id="navbarText">
                  <ul class="navbar-nav mr-auto">
                    <li class="nav-item">
                      <a class="nav-link" href="logout">Logout</a>
                    </li>
                    <li class="nav-item">
                      <a class="nav-link" href="home">Welcome, {{user.username}}</a>
                    </li>
                    {% else %}
                    <li class="nav-item">
                      <a class="nav-link" href="login">Login</a>
                    </li>
                    {% endif %}
                  </ul>
                </div>
              </nav>
            </ul>
        </div>
    </nav>
    <main id="main-holder">
        {% if user.is_authenticated %}
        <H1>Leveling and Chain Targets</H1>
        {% block content %}
        <div class="row">
          <div class="col-md">
          <div class="card card-body">
            <table cellspacing="5" cellpadding="5" border="0" class="centered-table" >
              <thead>
                <tr>
                    <td>Minimum Stats:</td>
                    <td><input type="text" id="min" name="min"></td>
                </tr>
                <tr>
                    <td>Maximum Stats:</td>
                    <td><input type="text" id="max" name="max"></td>
                </tr>
              </thead>
            </table>
            <table id="targetstable" class="styled-table">
              <thead>
                  <th>Username</th>
                  <th>UserID</th>
                  <th>User Status</th>
                  <th>TotalStats</th>
                  <th>Str</th>
                  <th>Def</th>
                  <th>Spd</th>
                  <th>Dex</th>
              </thead>
              {% for obj in tcp %}
              <tr data-user-id="{{obj.tcpuserid}}">
                  <td><a href="https://www.torn.com/profiles.php?XID={{obj.tcpuserid}}" target="_blank">{{obj.tcpusername}}</a></td>
                  <td>{{obj.tcpuserid}}</td>
                  <td>  
                    <div id="tdtargetstatus-{{obj.tcpuserid}}" class="target-status"></div>   
                  </td>
                  <td>{{obj.tcptotalstats}}</td>
                  <td>{{obj.tcpstr}}</td>
                  <td>{{obj.tcpdef}}</td>
                  <td>{{obj.tcpspd}}</td>
                  <td>{{obj.tcpdex}}</td>
              </tr>
              {% endfor %}
            </table>     
          </div>
          </div>
        </div>

        <script>
            // DataTables 范围过滤逻辑
            $.fn.dataTable.ext.search.push(function (settings, data, dataIndex) {
                var min = parseInt($('#min').val(), 10);
                var max = parseInt($('#max').val(), 10);
                var mxstats = parseFloat(data[3]) || 0; // 修正列索引:TotalStats是第4列,索引为3(从0开始)
            
                if (
                    (isNaN(min) && isNaN(max)) ||
                    (isNaN(min) && mxstats <= max) ||
                    (min <= mxstats && isNaN(max)) ||
                    (min <= mxstats && mxstats <= max)
                ) {
                    return true;
                }
                return false;
            });
            
            $(document).ready(function () {
                var table = $('#targetstable').DataTable();
            
                // 批量处理玩家状态API请求
                $('#targetstable tbody tr').each(function() {
                    var userId = $(this).data('user-id');
                    var statusContainer = $('#tdtargetstatus-' + userId);
                    
                    $.ajax({
                        url: "https://api.torn.com/user/" + userId + "?selections=&key=9iUftIRM7DydKZKE",
                        type: "GET",
                        success: function(result){
                            var statusText = result.status.description;
                            statusContainer.html(statusText);
                        },
                        error: function(){
                            statusContainer.html("获取状态失败");
                        }
                    });
                });
            
                // 范围过滤输入框监听
                $('#min, #max').keyup(function () {
                    table.draw();
                });
            });
        </script>
        {% endblock %}
        {% endif %}
    </main>
</body>
</html>

额外说明

  • 修正了DataTables过滤逻辑中的列索引错误:原来的data[2]对应User Status列,改为data[3]对应TotalStats列
  • 添加了Ajax请求的错误处理,避免状态容器为空
  • 给表格行添加了data-user-id属性,更优雅地获取玩家ID

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 15:39:35