如何在Django中从views.py向forms.ChoiceField传递选项实现在线测试表单
实现方案
核心思路是重写表单类的初始化方法,支持接收视图传递的动态参数,在表单初始化阶段给单选框字段赋值选项,不需要在forms.py中提前定义固定选项。
1. 编写动态选项表单(forms.py)
表单中单选字段的choices初始设为空列表,通过重写__init__方法接收视图传入的Course实例,从实例中提取测试题目选项,动态赋值给单选字段的choices参数:
from django import forms from .models import Course class TestForm(forms.Form): # 单选字段初始不设置固定选项 test_answer = forms.ChoiceField( label="测试题答案", choices=[], widget=forms.RadioSelect # 指定渲染为单选按钮 ) def __init__(self, *args, **kwargs): # 接收视图传入的course实例参数 self.course = kwargs.pop('course', None) super().__init__(*args, **kwargs) if self.course: # 从course实例中提取Test1_1到Test1_5作为选项 self.fields['test_answer'].choices = [ (str(i), getattr(self.course, f'Test1_{i}')) for i in range(1, 6) if getattr(self.course, f'Test1_{i}').strip() # 过滤空选项 ] # 可选:把测试题目设为字段label self.fields['test_answer'].label = self.course.Test1
2. 视图层传递动态参数(views.py)
无论是GET请求首次加载表单,还是POST请求提交后做表单校验,都需要先查询到对应的Course实例,再将实例传入表单的初始化参数中:
from django.shortcuts import render, get_object_or_404 from .models import Course from .forms import TestForm def course_test(request, course_slug): # 根据url传入的slug查询对应的课程实例 course = get_object_or_404(Course, slug=course_slug) if request.method == 'POST': # POST请求校验时也要传入course参数 form = TestForm(request.POST, course=course) if form.is_valid(): selected_answer = form.cleaned_data['test_answer'] # 此处添加答案校验、得分统计等业务逻辑 return render(request, 'test_result.html', {'score': 100}) else: # GET请求加载表单时传入course参数 form = TestForm(course=course) return render(request, 'test_page.html', {'form': form, 'course': course})
3. 模板渲染示例(test_page.html)
正常渲染表单即可,单选按钮会自动加载从Course实例中提取的动态选项:
<!DOCTYPE html> <html> <head> <title>{{ course.title }}测试</title> </head> <body> <h1>{{ course.title }}</h1> <form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit">提交答案</button> </form> </body> </html>
注意事项
- 如果需要支持多道测试题,可以在表单的
__init__方法中循环生成多个ChoiceField字段,适配不同的测试题数量 - POST请求必须传入course参数,否则表单初始化时没有选项数据,会触发校验错误
- 若选项存储结构调整,只需要修改表单
__init__中组装choices的逻辑即可,不需要改动视图层代码
内容的提问来源于stack exchange,提问作者mrKaban
相关产品推荐
相关产品推荐

