如何让Django循环渲染的各组单选按钮相互独立互不干扰
问题现象
通过for循环遍历employee_list中的员工对象,为每位员工渲染forms.py中定义的、包含"Present"/"Absent"两个选项的单选按钮表单时,出现跨员工单选框互斥问题:选中员工1的选项后,再选员工2的选项,员工1的选中状态会自动取消,无法为不同员工单独选择出勤状态。
相关代码
mark_attendance.html
{% extends 'base.html' %} {% block content %} <form action="{% url 'confirm' %}" method="post"> {% csrf_token %} <div class="card mb-3"> <div class="card-header"> <i class="fas fa-table"></i> <div class="card-body"> <div class="table-responsive"> <table class="table table-bordered" id="dataTable" width="100%" cellspacing="0"> <thead> <tr> <th>Employee name</th> <th></th> </tr> </thead> <tbody> {% for employee in employee_list %} {{<tr> <td>{{employee}}</td> <td> {{ form }} </td> </tr>}} {% endfor %} </tbody> </table> </div> </div> </div> <input class="btn btn-success" type="submit" value="Submit"> </form> {% endblock %}
forms.py
from django import forms class AttendanceForm(forms.Form): ATTENDANCE_CHOICES = [ ('Present', 'Present'), ('Absent', 'Absent'), ] decision = forms.CharField(label='Attendance', widget=forms.RadioSelect(choices=ATTENDANCE_CHOICES))
views.py
from django.shortcuts import render, get_object_or_404 from django.http import HttpResponseRedirect from django.contrib.auth.decorators import login_required from django.utils import timezone from .models import Attendance from django.urls import reverse from .models import Student from .forms import AttendanceForm from datetime import datetime employee_list = ["John", "Jane", "Jacob", "Mark", "Jingleheimer", "Rob", "Schmidt"] # Create your views here. @login_required def home(request): return render(request, 'index.html') @login_required def attendance(request): now = timezone.now().date status = 0 return render(request, 'attendance.html', { "date": now, "status": status, }) @login_required def mark_attendance(request): employee_list = Student.objects.all() if request.method == "POST": form = AttendanceForm(request.POST) for employee in employee_list: if form.is_valid(): decision = form.cleaned_data['decision'] status = Attendance(student=employee, date=datetime.now(), status=decision) status.save() return HttpResponseRedirect('mark_attendance.html') else: form = AttendanceForm() return render(request, 'mark_attendance.html', { "form": form, "employee_list": employee_list, }) # @login_required # def confirm(request): # status = request.POST.get('status') # print(status)
问题根因
- HTML单选框依靠
name属性做分组,同name的单选框同时只能选中一个。循环中反复渲染同一个AttendanceForm实例,所有单选框的name属性值均为decision,天然属于同一组,必然会出现互斥。 - 视图逻辑存在缺陷:仅实例化了一个表单,提交后只能拿到一个出勤值,无法匹配所有员工;且现有POST逻辑保存完第一个员工的数据就直接重定向,后续员工的出勤数据完全不会被处理。
- 模板存在语法错误:
<tr>标签被错误包裹在Django模板变量插值符号{{}}中,会导致表格渲染异常。
修复方案
使用Django原生的表单集(Formset)处理批量同类型表单,表单集会自动为每个子表单的字段添加独立前缀,保证单选框name不重复,同时自带批量校验逻辑。
1. 修改视图代码
导入formset_factory,替换原有单表单实例化逻辑,修正重定向、时区相关错误:
from django.forms import formset_factory # 其余原有导入保持不变 @login_required def mark_attendance(request): employee_list = Student.objects.all() # 生成和员工数量匹配的表单集 AttendanceFormSet = formset_factory(AttendanceForm, extra=len(employee_list)) if request.method == "POST": formset = AttendanceFormSet(request.POST) if formset.is_valid(): # 员工和表单一一对应保存 for employee, form in zip(employee_list, formset): decision = form.cleaned_data['decision'] Attendance.objects.create( student=employee, date=timezone.now().date(), status=decision ) # 用reverse解析路由,不要硬写模板路径 return HttpResponseRedirect(reverse('mark_attendance')) else: formset = AttendanceFormSet() # 提前打包员工和表单的配对关系,避免模板自定义过滤器 employee_form_pairs = list(zip(employee_list, formset)) return render(request, 'mark_attendance.html', { "formset": formset, "employee_form_pairs": employee_form_pairs, })
2. 修改模板代码
修正模板语法错误,循环渲染配对的员工和表单,补全未闭合的标签:
{% extends 'base.html' %} {% block content %} <form action="{% url 'confirm' %}" method="post"> {% csrf_token %} <div class="card mb-3"> <div class="card-header"> <i class="fas fa-table"></i> </div> <div class="card-body"> <div class="table-responsive"> <table class="table table-bordered" id="dataTable" width="100%" cellspacing="0"> <thead> <tr> <th>Employee name</th> <th>Attendance status</th> </tr> </thead> <tbody> {% for employee, form in employee_form_pairs %} <tr> <td>{{ employee }}</td> <td>{{ form.decision }}</td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> <input class="btn btn-success" type="submit" value="Submit"> </form> {% endblock %}
修复后每个员工对应的单选框会有独立的name属性(如form-0-decision、form-1-decision),不会再出现跨员工互斥的问题,提交后也能正常批量保存所有员工的出勤状态。
内容的提问来源于stack exchange,提问作者Skippy
相关产品推荐
相关产品推荐

