DJango How to build a formset for a Quiz?
I'm working on a quiz application that needs to display a quiz to be taken My models .py looks like this
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)
I'm trying to build a form or form-set that will let me have a form for a quiz attempt that contains all of the questions so it looks something like this, and will let me get that back in a view to save a student's attempt at a quiz.
- Question 1
- Option a
- ect
So far it looks like my best solution is to build a formset out of these models, but I'm not sure how to specify my choices to limit it to answers associated with the current question, or how to get the right formset in a view.
If I am understanding your question correctly, you'll probably want to create a custom Form
at execute time, and many custom Field
s. I would expect something along the lines of:
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.
It will likely take additional tweaking to make this work through the admin interface, but this should give you a good start for constructing the form itself at execution time.
Your view would probably look something like:
# 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})
This is a rough idea of how it should work.
# 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
精彩评论