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

在Django模板中用JS实现联动下拉框的动态数据填充

实现无刷新联动Select下拉框

问题背景

现有三级联动Select下拉框用于产品分类:

  1. 第一级选择分类方式(usage或model)
  2. 第二级根据第一级选择,显示对应分类的选项(Usage模型数据或MainModel模型数据)
  3. 第三级需要根据第二级选中值动态填充子选项,而非全量加载所有子数据(SubUsage或PumpType),要求无刷新实现。

当前已完成显示/隐藏逻辑,但第三级仍全量加载数据,需优化为动态填充。


解决方案

1. 后端修改(views.py)

生成以父级ID为键、子级数据列表为值的映射字典,转成JSON格式传给前端(避免全量加载子数据):

def get_common_queryset():
    usage_queryset = Usage.objects.all()
    main_model_queryset = MainModel.objects.all()
    
    # 构建Usage -> SubUsage的映射(键为usage.id字符串)
    sub_usage_map = {}
    for usage in usage_queryset:
        # 只取需要的字段,减少数据量
        sub_usages = SubUsage.objects.filter(usage=usage).values('id', 'sub_usage_name_fa')
        sub_usage_map[str(usage.id)] = list(sub_usages)
    
    # 构建MainModel -> PumpType的映射(键为model.id字符串)
    pump_type_map = {}
    for model in main_model_queryset:
        pump_types = PumpType.objects.filter(main_model=model).values('id', 'type_name')
        pump_type_map[str(model.id)] = list(pump_types)
    
    queryset_dictionary = {
        "usage_queryset": usage_queryset,
        "main_model_queryset": main_model_queryset,
        "sub_usage_map": sub_usage_map,
        "pump_type_map": pump_type_map,
    }
    return queryset_dictionary

def solution_main(request):
    context = get_common_queryset()
    return render(request, "solutions/solution_main.html", context)

2. 模板修改(solution_main.html)

  • 移除第三级Select的全量循环,仅保留默认选项
  • 添加脚本标签,将后端映射数据注入为JS变量

修改后的第三级Select代码:

{# select sub_usage or pump_type div #}
<div class="col-md-3 mx-md-5">
    {#  sub_usage select div #}
    <div class="sub_usage visually-hidden" id="sub_usage_div">
        <h2 class="h5 nm-text-color fw-bold mb-4">Select sub-usage:</h2>
        <select required aria-label="Select sub_usage" class="form-select" name="sub_usage_select" id="sub_usage_select">
            <option selected>All sub-usages from this usage</option>
        </select>
    </div>

    {#  pump type select div #}
    <div class="pump-type visually-hidden" id="pump_type_div">
        <h2 class="h5 nm-text-color fw-bold mb-4">Select pump type:</h2>
        <select aria-label="Select pump_type" class="form-select" name="pump_type_select" id="pump_type_select">
            <option selected>All pump types of this model</option>
        </select>
    </div>
</div>

在模板底部添加JS脚本:

<script>
    // 从后端获取预加载的映射数据(使用safe过滤器避免转义)
    const subUsageMap = {{ sub_usage_map|safe }};
    const pumpTypeMap = {{ pump_type_map|safe }};

    // 辅助函数:重置Select到初始状态(保留默认选项)
    function resetSelect(selectElement) {
        while (selectElement.options.length > 1) {
            selectElement.remove(1);
        }
        selectElement.selectedIndex = 0;
    }

    // 第一级Select切换逻辑:显示/隐藏第二级容器,重置后续Select
    function set_usage_or_model_dic() {
        const selectedValue = document.getElementById('usage_model_select').value;
        const usageDiv = document.getElementById('usage_div');
        const modelDiv = document.getElementById('model_div');
        const subUsageDiv = document.getElementById('sub_usage_div');
        const pumpTypeDiv = document.getElementById('pump_type_div');

        // 重置所有子Select和容器
        resetSelect(document.getElementById('sub_usage_select'));
        resetSelect(document.getElementById('pump_type_select'));
        subUsageDiv.classList.add('visually-hidden');
        pumpTypeDiv.classList.add('visually-hidden');

        // 切换第二级容器显示状态
        if (selectedValue === 'usage') {
            usageDiv.classList.remove('visually-hidden');
            modelDiv.classList.add('visually-hidden');
        } else if (selectedValue === 'model') {
            modelDiv.classList.remove('visually-hidden');
            usageDiv.classList.add('visually-hidden');
        } else {
            usageDiv.classList.add('visually-hidden');
            modelDiv.classList.add('visually-hidden');
        }
    }

    // 第二级Usage选择后,填充SubUsage选项
    function set_sub_usage_list() {
        const usageId = document.getElementById('usage_select_id').value;
        const subUsageSelect = document.getElementById('sub_usage_select');
        const subUsageDiv = document.getElementById('sub_usage_div');

        resetSelect(subUsageSelect);
        subUsageDiv.classList.add('visually-hidden');

        if (usageId && subUsageMap[usageId]) {
            subUsageDiv.classList.remove('visually-hidden');
            // 动态创建选项
            subUsageMap[usageId].forEach(item => {
                const option = document.createElement('option');
                option.value = item.id;
                option.textContent = item.sub_usage_name_fa;
                subUsageSelect.appendChild(option);
            });
        }
    }

    // 第二级Model选择后,填充PumpType选项
    function set_pump_type_list() {
        const modelId = document.getElementById('model_select_id').value;
        const pumpTypeSelect = document.getElementById('pump_type_select');
        const pumpTypeDiv = document.getElementById('pump_type_div');

        resetSelect(pumpTypeSelect);
        pumpTypeDiv.classList.add('visually-hidden');

        if (modelId && pumpTypeMap[modelId]) {
            pumpTypeDiv.classList.remove('visually-hidden');
            // 动态创建选项
            pumpTypeMap[modelId].forEach(item => {
                const option = document.createElement('option');
                option.value = item.id;
                option.textContent = item.type_name;
                pumpTypeSelect.appendChild(option);
            });
        }
    }
</script>

3. 关键说明

  • 后端用values()方法只获取需要的字段,减少传输数据量
  • 映射字典用父级ID作为键,前端选中后可直接通过ID取值,无需遍历
  • JS中使用document.createElement动态创建选项,避免拼接HTML字符串的安全问题
  • 切换时重置所有后续Select,避免残留旧数据

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 08:45:17