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

基于Python(Flask/PostgreSQL)实现DataTables服务器端处理问询

解决Flask+PostgreSQL大数据表格加载慢的服务器端分页方案

我来帮你搞定这个34000条数据加载慢的问题,服务器端分页是最直接有效的解决方案——每次只请求当前页的数据,避免一次性传输和渲染几万条记录。结合你的技术栈,我给你分步骤实现:

核心问题分析

你当前的代码是一次性把test表所有数据查询出来并渲染,这会导致三个大问题:

  1. PostgreSQL查询大量数据耗时久
  2. 服务器到前端的网络传输数据量过大
  3. 前端浏览器渲染几万条DOM节点卡顿

服务器端分页的核心思路是:每次只查询当前页需要的N条数据,同时获取总数据量用于计算分页页码。


方案一:传统页面刷新式分页(适配你现有Bootstrap分页JS)

这个方案不需要大改前端逻辑,通过URL参数传递页码,后端返回对应页的数据。

1. 修改Flask后端路由(main.py)

更新home函数,处理分页参数,用PostgreSQL的LIMIT和OFFSET实现分页查询:

from flask import request

@login_required
def home():
    # 获取分页参数,默认第1页,每页8条(和你现有JS的分页数量匹配)
    page = request.args.get('page', 1, type=int)
    per_page = request.args.get('per_page', 8, type=int)
    
    connect() # 连接PG数据库
    # 1. 查询当前页的数据
    connect.cur.execute("""
        SELECT * FROM test 
        LIMIT %s OFFSET %s
    """, (per_page, (page - 1) * per_page))
    test_execute = connect.cur.fetchall()
    
    # 2. 查询总数据条数,用于计算总页数
    connect.cur.execute("SELECT COUNT(*) FROM test")
    total_count = connect.cur.fetchone()[0]
    total_pages = (total_count + per_page - 1) // per_page  # 向上取整计算总页数
    
    # 保留你原有的统计逻辑
    count_equipement()
    
    return render_template(
        'index.html', 
        value=test_execute, 
        value2=count_equipement.nb_equipement, 
        value3=check_ok.nb_ok, 
        value4=check_ko.nb_ko,
        current_page=page,
        total_pages=total_pages,
        per_page=per_page,
        total_count=total_count
    )

2. 更新前端模板(index.html)

在表格下方添加分页组件,同时保留原有表格渲染逻辑:

<!-- 原有表格代码保持不变 -->
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
    <!-- ... 你的thead、tfoot、tbody代码 ... -->
</table>

<!-- 新增分页组件 -->
<nav aria-label="Page navigation" class="mt-4">
  <ul class="pagination justify-content-center">
    <!-- 上一页按钮 -->
    <li class="page-item {% if current_page == 1 %}disabled{% endif %}">
      <a class="page-link" href="{{ url_for('home', page=current_page-1, per_page=per_page) }}" aria-label="Previous">
        <span aria-hidden="true">&laquo;</span>
      </a>
    </li>
    
    <!-- 页码按钮(优化显示:只展示当前页前后2个页码,避免过多) -->
    {% set start_page = max(1, current_page - 2) %}
    {% set end_page = min(total_pages, current_page + 2) %}
    {% for p in range(start_page, end_page+1) %}
      {% if p == current_page %}
        <li class="page-item active"><a class="page-link" href="{{ url_for('home', page=p, per_page=per_page) }}">{{ p }}</a></li>
      {% else %}
        <li class="page-item"><a class="page-link" href="{{ url_for('home', page=p, per_page=per_page) }}">{{ p }}</a></li>
      {% endif %}
    {% endfor %}
    
    <!-- 下一页按钮 -->
    <li class="page-item {% if current_page == total_pages %}disabled{% endif %}">
      <a class="page-link" href="{{ url_for('home', page=current_page+1, per_page=per_page) }}" aria-label="Next">
        <span aria-hidden="true">&raquo;</span>
      </a>
    </li>
  </ul>
</nav>

<!-- 新增数据统计提示 -->
<div class="text-center text-muted">
  显示第 {{ (current_page-1)*per_page + 1 }} 到 {{ min(current_page*per_page, total_count) }} 条,共 {{ total_count }} 条数据
</div>

方案二:AJAX异步分页(无刷新更流畅)

如果想实现无刷新分页,体验更好,可以用AJAX动态加载数据,不用刷新整个页面。

1. 新增Flask API路由(main.py)

专门用于返回分页数据的JSON接口:

from flask import jsonify

@login_required
def test_data_api():
    page = request.args.get('page', 1, type=int)
    per_page = request.args.get('per_page', 8, type=int)
    
    connect()
    # 查询当前页数据
    connect.cur.execute("""
        SELECT * FROM test 
        LIMIT %s OFFSET %s
    """, (per_page, (page - 1) * per_page))
    data = connect.cur.fetchall()
    
    # 查询总数据量
    connect.cur.execute("SELECT COUNT(*) FROM test")
    total_count = connect.cur.fetchone()[0]
    total_pages = (total_count + per_page - 1) // per_page
    
    # 格式化数据为JSON友好的结构
    formatted_data = []
    for row in data:
        formatted_data.append({
            'col1': row[0],
            'col2': row[1],
            'col3': row[2],
            'col4': row[3],
            'col5': row[4]
        })
    
    return jsonify({
        'data': formatted_data,
        'current_page': page,
        'total_pages': total_pages,
        'total_count': total_count
    })

记得在Flask app中注册这个路由:

app.add_url_rule('/test-data', 'test_data_api', test_data_api)

2. 前端AJAX实现(index.html)

修改表格和分页部分,用JS动态渲染:

<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
    <thead>
        <tr>
            <th>First</th>
            <th>Second</th>
            <th>Third</th>
            <th>Fourth</th>
            <th>Fifth</th>
        </tr>
    </thead>
    <tfoot>
        <tr>
            <th>First</th>
            <th>Second</th>
            <th>Third</th>
            <th>Fourth</th>
            <th>Fifth</th>
        </tr>
    </tfoot>
    <tbody id="tableBody">
        <!-- 内容由JS动态加载 -->
    </tbody>
</table>

<!-- 分页容器 -->
<nav aria-label="Page navigation" class="mt-4">
  <ul class="pagination justify-content-center" id="pagination">
    <!-- 内容由JS动态生成 -->
  </ul>
</nav>

<script>
// 初始化加载第一页
loadPage(1);

function loadPage(page) {
    const per_page = 8;
    fetch(`/test-data?page=${page}&per_page=${per_page}`)
        .then(response => response.json())
        .then(res => {
            // 渲染表格内容
            const tableBody = document.getElementById('tableBody');
            tableBody.innerHTML = '';
            res.data.forEach(row => {
                const tr = document.createElement('tr');
                tr.innerHTML = `
                    <td>${row.col1}</td>
                    <td><a href="{{ url_for('site', site_id='') }}${row.col2}">${row.col2}</a></td>
                    <td>${row.col3}</td>
                    <td>${row.col4}</td>
                    <td>${row.col5}</td>
                `;
                tableBody.appendChild(tr);
            });
            
            // 渲染分页按钮
            const pagination = document.getElementById('pagination');
            pagination.innerHTML = '';
            
            // 上一页按钮
            const prevLi = document.createElement('li');
            prevLi.className = `page-item ${res.current_page === 1 ? 'disabled' : ''}`;
            prevLi.innerHTML = `<a class="page-link" href="#" onclick="loadPage(${res.current_page - 1})" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a>`;
            pagination.appendChild(prevLi);
            
            // 页码按钮
            const startPage = Math.max(1, res.current_page - 2);
            const endPage = Math.min(res.total_pages, res.current_page + 2);
            for (let p = startPage; p <= endPage; p++) {
                const li = document.createElement('li');
                li.className = `page-item ${p === res.current_page ? 'active' : ''}`;
                li.innerHTML = `<a class="page-link" href="#" onclick="loadPage(${p})">${p}</a>`;
                pagination.appendChild(li);
            }
            
            // 下一页按钮
            const nextLi = document.createElement('li');
            nextLi.className = `page-item ${res.current_page === res.total_pages ? 'disabled' : ''}`;
            nextLi.innerHTML = `<a class="page-link" href="#" onclick="loadPage(${res.current_page + 1})" aria-label="Next"><span aria-hidden="true">&raquo;</span></a>`;
            pagination.appendChild(nextLi);
        })
        .catch(err => console.error('加载数据失败:', err));
}
</script>

额外优化建议

  1. 避免重复连接数据库:不要每次请求都调用connect(),改用连接池(比如psycopg2.pool.SimpleConnectionPool),减少数据库连接开销。
  2. **不要用SELECT ***:只查询你需要的列,比如SELECT col1, col2, col3 FROM test,减少数据传输量。
  3. 添加数据库索引:如果后续需要按某些列筛选/排序,提前给这些列加索引,提升查询速度。
  4. 适配DataTables(如果你的Bootstrap主题用了这个):如果你的dataTable是DataTables插件,直接开启它的serverSide模式,配置更简单,官方文档有完整的服务器端处理示例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:37:31