如何在Django中实现同一页面多表单提交及多评分标准创建
如何在Django同一页面实现多评分标准表单的提交与存储
嘿,我明白你想要实现讲师能在一个页面里添加任意数量的评分标准,提交后全部存入数据库的需求。Django的**表单集(Formset)**正好是干这个的,下面我一步步给你讲怎么实现:
1. 定义表单集类
首先,我们基于你已有的MarkingCriteria表单来创建表单集。在你的forms.py里添加:
from django.forms import formset_factory # 创建表单集,默认显示2个空白表单,可按需调整extra参数 MarkingCriteriaFormSet = formset_factory(MarkingCriteria, extra=2)
这里的extra=2表示页面默认渲染2个空白表单,后面我们还会用JS实现动态添加更多表单的功能,满足任意数量的需求。
2. 编写视图函数
接下来处理GET和POST请求。假设讲师是在某个作业的详情页添加评分标准,我们可以从URL里获取作业ID,自动给每个评分标准关联对应的作业,不用让讲师手动选择:
from django.shortcuts import get_object_or_404, render, redirect from .models import AssignAssignment, Marking from .forms import MarkingCriteriaFormSet def add_marking_criteria(request, assignment_id): assignment = get_object_or_404(AssignAssignment, id=assignment_id) if request.method == 'POST': formset = MarkingCriteriaFormSet(request.POST) if formset.is_valid(): # 遍历每个表单,保存时自动关联当前作业 for form in formset: if form.cleaned_data: # 跳过未填写的空表单 marking = form.save(commit=False) marking.assignment = assignment marking.save() # 保存完成后跳回作业详情页 return redirect('assignment_detail', assignment_id=assignment_id) else: # 初始化表单集,预填assignment字段(配合后面的隐藏字段使用) formset = MarkingCriteriaFormSet(initial=[{'assignment': assignment} for _ in range(2)]) return render(request, 'add_marking_criteria.html', {'formset': formset, 'assignment': assignment})
这里有个实用优化:把MarkingCriteria表单里的assignment字段改成隐藏字段,避免页面显示冗余选项,修改你的表单类:
from django import forms from .models import Marking class MarkingCriteria(forms.ModelForm): class Meta: model = Marking fields = ('criteria', 'marks', 'assignment') widgets = { 'assignment': forms.HiddenInput() # 隐藏该字段,不显示在页面上 }
3. 编写模板(add_marking_criteria.html)
模板里要渲染表单集的所有表单,还要包含表单集的管理表单(Django用来跟踪表单数量的隐藏字段),再加上JS实现动态添加新表单的按钮:
<h2>为作业 "{{ assignment.title }}" 添加评分标准</h2> <form method="post"> {% csrf_token %} {{ formset.management_form }} <!-- 必须包含这个,Django依赖它管理表单集 --> <div id="formset-container"> {% for form in formset %} <div class="form-group"> {{ form.as_p }} <!-- 显示表单错误提示 --> {% if form.errors %} <div class="text-danger mt-2"> {% for field, errors in form.errors.items %} {% for error in errors %} {{ error }} {% endfor %} {% endfor %} </div> {% endif %} </div> <hr class="my-4"> {% endfor %} </div> <button type="button" id="add-form" class="btn btn-outline-secondary me-2">添加更多评分标准</button> <button type="submit" class="btn btn-primary">保存所有评分标准</button> </form> <script> document.addEventListener('DOMContentLoaded', function() { const addFormBtn = document.getElementById('add-form'); const formsetContainer = document.getElementById('formset-container'); const totalFormsInput = document.getElementById('id_form-TOTAL_FORMS'); let formCount = parseInt(totalFormsInput.value); addFormBtn.addEventListener('click', function() { // 复制第一个表单的结构 const newForm = formsetContainer.children[0].cloneNode(true); // 清空输入框内容并更新表单索引 newForm.querySelectorAll('input').forEach(input => { input.value = ''; // 替换name和id里的旧索引为新的formCount input.name = input.name.replace(/form-\d+-/, `form-${formCount}-`); input.id = input.id.replace(/form-\d+-/, `form-${formCount}-`); }); // 清空复制过来的错误提示 const errorElement = newForm.querySelector('.text-danger'); if (errorElement) errorElement.remove(); // 添加新表单到容器 formsetContainer.appendChild(newForm); // 更新总表单数量 totalFormsInput.value = ++formCount; }); }); </script>
4. 配置URL
最后在urls.py里添加对应的路由:
from django.urls import path from .views import add_marking_criteria urlpatterns = [ # 其他路由... path('assignment/<int:assignment_id>/add-marking/', add_marking_criteria, name='add_marking_criteria'), ]
这样讲师访问该URL就能看到默认的2个评分标准表单,点击“添加更多评分标准”按钮可以无限增加表单,提交后所有填写的评分标准都会自动关联到对应的作业并保存到数据库里啦!
内容的提问来源于stack exchange,提问作者mbdow
相关产品推荐
相关产品推荐

