如何在表格列中显示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
相关产品推荐
相关产品推荐

