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

如何为Django模型驱动的HTML表格添加行展开功能?

Django表格添加行展开/折叠功能解决方案

问题背景

用Django循环SQL模型数据生成了表格,希望给每行添加展开按钮,点击后显示另一模型中匹配household id的多行数据。尝试过多种jQuery函数和新增tbody循环数据的方式,均未达到预期效果。

预期效果

点击每行的展开按钮后,在当前行下方展开一个子表格,展示对应household的关联数据,支持再次点击收起。

解决方案

方案一:预加载关联数据(适合数据量小的场景)

直接在Django模板中预先生成子行并隐藏,通过jQuery控制显示/隐藏状态。

1. 修改模板代码

<div class="container" style="height:600px;width:1200px;overflow:scroll;max-height:500px">
    <table position="relative" class="table table-bordered">
        <thead>
            <tr>
                <th>操作</th>
                <th>Household</th>
                <th>Name</th>
                <th>Account Number</th>
                <th>Basic Materials %</th>
                <th>Communication Services %</th>
                <th>Consumer Cyclical %</th>
                <th>Consumer Defensive %</th>
                <th>Energy %</th>
                <th>Financial Services %</th>
                <th>Healthcare %</th>
                <th>Industrials %</th>
                <th>Real Estate %</th>
                <th>Technology %</th>
                <th>Utilities %</th>
            </tr>
        </thead>
        <tbody>
            {% for sector in sectors %}
            <!-- 主数据行 -->
            <tr class="parent-row" id="row-{{ sector.id }}">
                <td>
                    <button class="expand-btn" data-target="{{ sector.id }}">+</button>
                </td>
                <td>{{ sector.household }}</td>
                <td>{{ sector.owner }}</td>
                <td>{{ sector.account_num }}</td>
                <td>{{ sector.basic_materials }}</td>
                <td>{{ sector.communication_services }}</td>
                <td>{{ sector.consumer_cyclical }}</td>
                <td>{{ sector.consumer_defensive }}</td>
                <td>{{ sector.energy }}</td>
                <td>{{ sector.financial_services }}</td>
                <td>{{ sector.healthcare }}</td>
                <td>{{ sector.industrials }}</td>
                <td>{{ sector.real_estate }}</td>
                <td>{{ sector.technology }}</td>
                <td>{{ sector.utilities }}</td>
            </tr>
            <!-- 子数据行:初始隐藏,跨所有列 -->
            <tr class="child-row" data-parent="{{ sector.id }}" style="display:none;">
                <td colspan="15">
                    <table class="table table-sm table-bordered">
                        <thead>
                            <tr>
                                <!-- 替换为子模型的实际表头字段 -->
                                <th>关联字段1</th>
                                <th>关联字段2</th>
                                <th>关联字段3</th>
                            </tr>
                        </thead>
                        <tbody>
                            {% for related in sector.relatedmodel_set.all %}
                            <tr>
                                <td>{{ related.field1 }}</td>
                                <td>{{ related.field2 }}</td>
                                <td>{{ related.field3 }}</td>
                            </tr>
                            {% empty %}
                            <tr>
                                <td colspan="3">无关联数据</td>
                            </tr>
                            {% endfor %}
                        </tbody>
                    </table>
                </td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
</div>

2. 添加jQuery交互代码

$(document).ready(function() {
    $('.expand-btn').click(function() {
        const targetId = $(this).data('target');
        const childRow = $('.child-row[data-parent="' + targetId + '"]');
        
        // 切换子行显示/隐藏
        childRow.toggle();
        // 切换按钮文本
        $(this).text(childRow.is(':visible') ? '-' : '+');
    });
});

3. 优化查询性能

在Django视图中预取关联数据,避免N+1查询:

# views.py
from .models import Sector

def your_view(request):
    # 替换relatedmodel_set为实际的反向关联名称
    sectors = Sector.objects.prefetch_related('relatedmodel_set').all()
    return render(request, 'your_template.html', {'sectors': sectors})

方案二:AJAX动态加载(适合数据量大的场景)

点击展开按钮时,通过AJAX请求后端接口获取关联数据,动态生成子表格。

1. 模板修改(主行保留,子行留空)

<!-- 主行代码同方案一 -->
<tr class="child-row" data-parent="{{ sector.id }}" style="display:none;">
    <td colspan="15"></td>
</tr>

2. jQuery代码(含AJAX请求)

$(document).ready(function() {
    $('.expand-btn').click(function() {
        const $btn = $(this);
        const targetId = $btn.data('target');
        const childRow = $('.child-row[data-parent="' + targetId + '"]');
        
        // 已展开则直接收起
        if (childRow.is(':visible')) {
            childRow.hide();
            $btn.text('+');
            return;
        }
        
        // 未加载数据则发起AJAX请求
        if (childRow.html().trim() === '') {
            $.ajax({
                url: `/get-related-data/${targetId}/`,
                method: 'GET',
                success: function(data) {
                    let tableHtml = '<table class="table table-sm table-bordered"><thead><tr><th>关联字段1</th><th>关联字段2</th><th>关联字段3</th></tr></thead><tbody>';
                    if (data.length === 0) {
                        tableHtml += '<tr><td colspan="3">无关联数据</td></tr>';
                    } else {
                        data.forEach(item => {
                            tableHtml += `<tr><td>${item.field1}</td><td>${item.field2}</td><td>${item.field3}</td></tr>`;
                        });
                    }
                    tableHtml += '</tbody></table>';
                    childRow.html(tableHtml);
                },
                error: function() {
                    childRow.html('<p class="text-danger">加载数据失败</p>');
                }
            });
        }
        
        childRow.show();
        $btn.text('-');
    });
});

3. 后端Django视图和路由

# views.py
from django.http import JsonResponse
from .models import Sector, RelatedModel

def get_related_data(request, sector_id):
    sector = Sector.objects.get(id=sector_id)
    # 按household匹配关联数据
    related_data = RelatedModel.objects.filter(household=sector.household).values('field1', 'field2', 'field3')
    return JsonResponse(list(related_data), safe=False)
# urls.py
from django.urls import path
from . import views

urlpatterns = [
    # 其他路由
    path('get-related-data/<int:sector_id>/', views.get_related_data, name='get_related_data'),
]

注意事项

  • 子行的colspan值必须与表头的总列数一致,避免布局错乱。
  • 替换代码中的relatedmodel_set、field1/2/3等为实际的模型关联名称和字段名。
  • 确保已引入jQuery库(可通过CDN或本地文件)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 20:57:19