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

如何在Django中创建含未知数量选择框的表单?

用Django Formsets处理动态数量选择框的方案

1. 先明确模型与需求匹配(基于常见场景补充)

假设你的models.py结构如下(如果和实际有出入,可调整字段逻辑):

# models.py
from django.db import models

class Class(models.Model):
    name = models.CharField(max_length=100)

class Student(models.Model):
    name = models.CharField(max_length=100)
    cls = models.ForeignKey(Class, on_delete=models.CASCADE, related_name='students')

你需要为指定班级的每个学生生成一个选择框(比如成绩、状态选择),这类场景用普通Formset比Inline Formsets更合适——后者主要用于动态增删关联模型实例,而我们是基于已有学生批量生成表单控件。

2. 定义基础表单类

创建单个学生对应的表单,包含隐藏的学生ID和选择字段:

# forms.py
from django import forms

class StudentSelectionForm(forms.Form):
    student_id = forms.IntegerField(widget=forms.HiddenInput())
    # 替换为你的实际选择项,比如成绩、考勤状态等
    selection = forms.ChoiceField(choices=[('A', '优'), ('B', '良'), ('C', '及格'), ('D', '不及格')])

3. 生成Formset类

用formset_factory创建表单集合,设置extra=0避免默认生成空表单:

# forms.py 继续添加
from django.forms import formset_factory

StudentSelectionFormSet = formset_factory(StudentSelectionForm, extra=0)

4. 视图层初始化与处理表单

根据班级学生数量动态填充Formset的初始数据,处理提交后的逻辑:

# views.py
from django.shortcuts import render, redirect
from .models import Class
from .forms import StudentSelectionFormSet

def class_student_selection(request, class_id):
    current_class = Class.objects.get(id=class_id)
    students = current_class.students.all()

    if request.method == 'POST':
        formset = StudentSelectionFormSet(request.POST)
        if formset.is_valid():
            # 遍历处理每个学生的选择数据
            for form_data in formset.cleaned_data:
                student_id = form_data['student_id']
                selected_value = form_data['selection']
                # 这里写入你的业务逻辑,比如保存到关联表、更新学生状态等
                print(f"学生ID {student_id} 的选择结果:{selected_value}")
            return redirect('class_list') # 替换为你的跳转路径
    else:
        # 为每个学生生成初始表单数据
        initial_data = [{'student_id': student.id} for student in students]
        formset = StudentSelectionFormSet(initial=initial_data)

    return render(request, 'student_selection.html', {
        'formset': formset,
        'current_class': current_class
    })

5. 模板渲染表单(自定义name/id可选)

方式1:用Django自动生成的name/id(推荐)

模板中必须包含formset.management_form,它负责管理表单数量的隐藏字段:

<!-- student_selection.html -->
<form method="post">
  {% csrf_token %}
  {{ formset.management_form }}
  
  {% for form in formset %}
    {{ form.student_id }} <!-- 渲染隐藏的学生ID字段 -->
    <div>
      <label>
        {{ current_class.students.get(id=form.student_id.value).name }}
      </label>
      {{ form.selection }}
      {% if form.selection.errors %}
        <span class="text-red-500">{{ form.selection.errors.0 }}</span>
      {% endif %}
    </div>
  {% endfor %}
  
  <button type="submit">提交</button>
</form>

方式2:自定义name/id属性(匹配原模板格式)

如果需要和你原HTML的name="student-xxx"、id="student-xxx"格式一致,可修改表单初始化逻辑:

# 修改forms.py中的StudentSelectionForm
class StudentSelectionForm(forms.Form):
    student_id = forms.IntegerField(widget=forms.HiddenInput())
    selection = forms.ChoiceField(
        choices=[('A', '优'), ('B', '良'), ('C', '及格'), ('D', '不及格')],
        widget=forms.Select()
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 初始化时替换为自定义的name和id
        if student_id := self.initial.get('student_id'):
            self.fields['selection'].widget.attrs.update({
                'id': f'student-{student_id}',
                'name': f'student-{student_id}'
            })

模板渲染逻辑和方式1一致,最终生成的选择框会和你原模板的属性格式完全匹配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 04:55:25