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

如何用HTMX动态添加Django Formset实例并正确提交POST请求?

解决Django嵌套动态Formset结合HTMX时仅最后一个子表单数据被提交的问题

问题核心原因

每次通过HTMX调用addStructure视图时,返回的都是全新的StructureFormset实例,所有表单字段的前缀都是form-0。POST提交时,重复的字段名会导致Django仅保留最后一个实例的数据,而忽略之前的所有表单。正常的Django Formset是通过递增的索引前缀(如form-0、form-1、form-2)来区分不同表单实例的。

解决方案步骤

1. 前端传递当前表单计数给HTMX请求

修改plan_forms.html中的"Add structure"链接,通过hx-vals传递当前已有的子表单数量,让后端生成对应索引的空表单:

<a href="" 
   hx-get="{% url 'structure-form' %}" 
   hx-vals='{"form_count": document.querySelectorAll("#structureforms tr").length}' 
   hx-swap="beforeend" 
   hx-target="#structureforms">
  Add structure
</a>

2. 后端根据计数生成带正确索引的空表单

修改views.py中的addStructure视图,接收前端传递的form_count参数,生成对应索引前缀的单个空表单(而非整个formset):

def addStructure(request):
    form_count = int(request.GET.get('form_count', 0))
    # 初始化formset并获取空表单模板
    sformset = StructureFormset(queryset=Structure.objects.none(), prefix='form')
    empty_form = sformset.empty_form
    # 修改空表单的前缀索引,确保唯一性
    empty_form.prefix = f'form-{form_count}'
    
    context = {"form": empty_form}
    return render(request, 'app/formsets/structure_form.html', context)

3. 修改子表单模板渲染单个表单

更新formsets/structure_form.html,渲染单个表单而非整个formset:

<tr>
  <td class="col-12 px-1">
    {{ form.as_table }} <!-- 可根据需求替换为.as_p或.as_ul -->
  </td>
</tr>

4. 修正父视图的Formset初始化

修改createPlan视图,指定formset的prefix参数,确保Django能正确识别所有动态添加的表单字段:

def createPlan(request):
    form = PlanForm(request.POST or None)
    # 指定prefix与前端保持一致,确保能解析所有动态表单
    sformset = StructureFormset(request.POST or None, queryset=Structure.objects.none(), prefix='form')

    context = {
        'form': form,
        'sformset': sformset,
    }

    if request.method == 'POST':
        if form.is_valid() and sformset.is_valid():
            plan = form.save()
            # 批量关联子表单到父模型
            for structure in sformset.save(commit=False):
                structure.plan = plan
                structure.save()
            # 提交后可重定向到列表页或当前页
            return redirect('create_plan')

    return render(request, 'app/plan_forms.html', context)

5. 初始化渲染默认子表单

在plan_forms.html的structureforms tbody中,添加初始表单的渲染逻辑:

<tbody style="user-select: none;" id="structureforms" hx-sync="closest form:queue">
  {% for form in sformset %}
  <tr>
    <td class="col-12 px-1">{{ form.as_table }}</td>
  </tr>
  {% endfor %}
</tbody>

效果说明

修改后,每次点击"Add structure"都会生成带唯一索引前缀的子表单(form-0、form-1...),POST提交时Django能正确识别所有子表单数据,不会出现覆盖问题,同时保持了HTMX无JS(除了内置API)的特性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 14:20:27