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

如何将Django Formset以列格式展示并保留原有JavaScript功能?

解决Django Formset列布局+保留JS功能的方案

看起来你已经搞定了Formset的JS动态功能,现在就差把表单改成列格式啦~ 我来给你两种常见的列布局方案,都能完美兼容你现有的JS功能:

方案1:让Formset的每个表单实例并排显示(一行N个表单)

适合你想把多个表单条目横向排列的场景,比如一行放2个表单:

修改后的HTML代码

<div class='col-sm-10 col-sm-offset-1'>
  <h1> Enter scheme of entries</h1>
  <form action='' method='POST'> {% csrf_token %}
    <div class='btn-group mb-3'>
      <input class='btn btn-primary' type='submit' value='Save' />
      <a class='btn btn-default' href='/'>Cancel</a>
    </div>

    {{ formset.management_form }}
    <!-- 用row包裹所有表单,每个表单占半列 -->
    <div id="formset-container" class="row g-4">
      {% for form in formset %}
        <div class="formset-item col-sm-6">
          {{ form }}
          <!-- 如果开启了删除功能,添加删除按钮 -->
          {% if formset.can_delete %}
            <button type="button" class="btn btn-danger remove-form mt-2">Remove</button>
          {% endif %}
        </div>
      {% endfor %}
    </div>

    <button type="button" id="add-form" class="btn btn-success mt-3">Add Another Entry</button>
  </form>
</div>

调整对应的JS代码(确保新增表单也带列布局)

如果你的原有JS是处理添加/删除表单的,只要修改克隆逻辑,让新表单也包裹在col-sm-6的容器里:

document.addEventListener('DOMContentLoaded', function() {
  const addBtn = document.getElementById('add-form');
  const formContainer = document.getElementById('formset-container');
  const totalFormsInput = document.getElementById('id_form-TOTAL_FORMS');
  let formCount = parseInt(totalFormsInput.value);

  // 新增表单逻辑
  addBtn.addEventListener('click', function() {
    // 克隆第一个带列布局的表单容器
    const newFormWrapper = document.querySelector('.formset-item').cloneNode(true);
    // 替换所有字段的索引(把form-0改成form-[当前计数])
    newFormWrapper.innerHTML = newFormWrapper.innerHTML.replace(/form-\d+/g, `form-${formCount}`);
    
    // 清空新表单的输入值
    newFormWrapper.querySelectorAll('input, select, textarea').forEach(field => {
      field.value = '';
      field.checked = false;
      field.selectedIndex = 0;
    });

    // 给新的删除按钮绑定事件
    newFormWrapper.querySelector('.remove-form').addEventListener('click', function() {
      newFormWrapper.remove();
      totalFormsInput.value = parseInt(totalFormsInput.value) - 1;
    });

    // 把新表单加入容器
    formContainer.appendChild(newFormWrapper);
    totalFormsInput.value = ++formCount;
  });

  // 初始化现有删除按钮
  document.querySelectorAll('.remove-form').forEach(btn => {
    btn.addEventListener('click', function() {
      btn.closest('.formset-item').remove();
      totalFormsInput.value = parseInt(totalFormsInput.value) - 1;
    });
  });
});

方案2:单个表单的字段分栏显示

如果你想让每个表单内部的字段左右排列(比如一个表单里的两个字段各占半列),可以这样写:

HTML代码片段(替换Formset循环部分)

{{ formset.management_form }}
<div id="formset-container" class="space-y-4">
  {% for form in formset %}
    <div class="formset-item p-3 border rounded">
      <div class="row g-3">
        <!-- 第一个字段占半列 -->
        <div class="col-sm-6">
          {{ form.field_one.label_tag }}
          {{ form.field_one }}
        </div>
        <!-- 第二个字段占半列 -->
        <div class="col-sm-6">
          {{ form.field_two.label_tag }}
          {{ form.field_two }}
        </div>
        <!-- 第三个字段占整列 -->
        <div class="col-sm-12">
          {{ form.field_three.label_tag }}
          {{ form.field_three }}
        </div>
      </div>
      {% if formset.can_delete %}
        <button type="button" class="btn btn-danger remove-form mt-2">Remove</button>
      {% endif %}
    </div>
  {% endfor %}
</div>

对应的JS代码和方案1基本一致,因为克隆的时候会保留内部的row/col结构,只要确保字段索引更新正确就行。

关键注意点

  1. 一定要保留{{ formset.management_form }},这是Django Formset识别表单数量的核心。
  2. JS里的字段索引替换(form-\d+)必须正确,否则新增的表单数据不会被后端识别。
  3. 用Bootstrap的row和col-*类来控制布局,避免自定义CSS破坏原有样式。

这样调整后,既能实现你想要的列格式,又完全保留原有JS的动态添加/删除功能~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:12:33