Django 如何为测验构建表单集?
我正在开发一个测验应用程序,需要展示一个可以进行的测验。我的模型文件(models.py)看起来是这样的:
from django.db import models
from django.contrib.auth.models import User
from django.contrib import admin
#######################
#Quiz Structure Models#
#######################
class Quiz(models.Model):
name = models.CharField(max_length = 255)
creation = models.DateField(auto_now_add=True)
creator = models.ForeignKey(User)
def __unicode__ (self):
return self.name
def possible(self):
total = 0
for question in self.question_set.all():
question.save()
total += question.value
return total
class Question(models.Model):
question = models.CharField(max_length = 255)
quiz = models.ForeignKey(Quiz)
creator = models.ForeignKey(User)
creation = models.DateField(auto_now_add = True)
#objective = TODO: include standards linking in later versions
value = models.IntegerField(default = 1)
def __unicode__(self):
return self.question
class Answer(models.Model):
answer = models.CharField(max_length = 255)
question = models.ForeignKey(Question)
is_correct = models.BooleanField(default = False)
#Creator is tied to the quiz
##########
#Attempts#
##########
class QuizAttempt(models.Model):
student = models.ForeignKey(User)
quiz = models.ForeignKey(Quiz)
date = models.DateField(auto_now_add = True)
#Score Method (similar to possible in Quiz
class QuestionAttempt(models.Model):
attempt = models.ForeignKey(QuizAttempt)
question = models.ForeignKey(Question)
response = models.ForeignKey(Answer)
#######
#Admin#
#######
class QuestionInline(admin.StackedInline):
model = Question
extra = 2
class AnswerInline(admin.StackedInline):
model = Answer
extra = 2
class QuizAdmin(admin.ModelAdmin):
list_display = ('name', 'creator', 'creation', 'possible',)
search_fields = ('name', 'creator')
inlines = [QuestionInline]
admin.site.register(Quiz, QuizAdmin)
class QuestionAdmin(admin.ModelAdmin):
inlines = [AnswerInline]
search_fields = ('question', 'quiz', 'value',)
list_display = ('question', 'quiz', 'value',)
admin.site.register(Question, QuestionAdmin)
我想创建一个表单或者表单集,这样我就可以为每次测验尝试准备一个表单,里面包含所有的问题,格式大致如下,并且可以在视图中获取这些信息,以便保存学生的测验尝试。
- 问题 1
- 选项 a
- 等等
到目前为止,我觉得最好的解决方案是用这些模型构建一个表单集,但我不太确定如何指定我的选项,以限制为当前问题相关的答案,或者如何在视图中获取正确的表单集。
2 个回答
2
这是一个大概念,说明它应该如何运作。
# define a form
class QuestionForm(forms.Form):
id = forms.IntegerField(widget=forms.HiddenInput) # make it hidden- i know, not very elegant
question = forms.CharField()
answer = forms.CharField()
# views.py
def display(request):
quiz = Quiz.objects.get(creator=request.user) # or some definition of quiz
questions = quiz.question_set.all().values('id','question') # to get question text
# define a formset
QuestionFormSet = formset_factory(QuestionForm)
# add initial data
formset = QuestionFormSet(initial=questions)
# should work because the field names are the same as that of form
if request.method == 'POST':
formset = QuestionFormSet(request.POST)
if formset.is_valid():
# associate answers here, note that you have access to the question id,
# which is a hidden field in your form
5
如果我理解你的问题没错的话,你可能想在执行时创建一个自定义的 Form
,还有很多自定义的 Field
。我预计你会需要做一些类似这样的事情:
class QuizForm(forms.Form):
def __init__(self, data, questions, *args, **kwargs):
self.questions = questions
for question in questions:
field_name = "question_%d" % question.pk
choices = []
for answer in question.answer_set().all():
choices.append((answer.pk, answer.answer,))
## May need to pass some initial data, etc:
field = forms.ChoiceField(label=question.question, required=True,
choices=choices, widget=forms.RadioSelect)
return super(QuizForm, self).__init__(data, *args, **kwargs)
def save(self):
## Loop back through the question/answer fields and manually
## update the Attempt instance before returning it.
为了让这个在管理界面上正常工作,可能还需要做一些额外的调整,但这应该能给你一个好的起点,让你在执行时构建表单。
你的视图可能看起来像这样:
# Assuming something like: /quiz/69/ with "69" being the quiz PK.
def render_quiz(request, quiz_id):
quiz = get_object_or_404(Quiz, quiz_id)
form = QuizForm(questions=quiz.question_set.all())
if request.method == "POST":
form = QuizForm(request.POST, questions=quiz.question_set.all())
if form.is_valid(): ## Will only ensure the option exists, not correctness.
attempt = form.save()
return redirect(attempt)
return render_to_response('quiz.html', {"form": form})