开发者

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.

  1. 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 Fields. 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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜