Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 701
TicViking Avatar asked Nov 15 '25 12:11

TicViking


1 Answers

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})
like image 174
Jack M. Avatar answered Nov 18 '25 10:11

Jack M.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!