7
\$\begingroup\$

I am creating simple quiz app in Django. It works, but I think that way I've done it is not the best. I have Exam detail view, which display questions related to that exam and answers for these questions. What are your suggests about it? I pasted only models, because I think that this is the problem here.

from django.db import models


class Answer(models.Model):
    """
    Answer's Model, which is used as the answer in Question Model
    """
    text = models.CharField(max_length=128, verbose_name=u'Answer\'s text')
    is_valid = models.BooleanField(default=False)

    def __str__(self):
        return self.text


class Question(models.Model):
    question_text = models.CharField(max_length=256, verbose_name=u'Question\'s text')
    answers = models.ManyToManyField(Answer)
    is_published = models.BooleanField(default=False)

    def __str__(self):
        return "{content} - {published}".format(content=self.question_text, published=self.is_published)

class Exam(models.Model):
    """
    Exam's model, works as a wrapper for the questions
    """
    name = models.CharField(max_length=64, verbose_name=u'Exam name', )
    slug = models.SlugField()
    questions = models.ManyToManyField(Question)

    def __str__(self):
        return self.name
\$\endgroup\$
  • 4
    \$\begingroup\$ Welcome to Code Review. Even though we could review only the models, I think we would get a better grasp at what you're trying to achieve if you included both views.py and admin.py. You’ll get better reviews as well. \$\endgroup\$ – 301_Moved_Permanently Dec 24 '15 at 9:36
8
\$\begingroup\$

As mentioned in the comments, it would be useful if you included more than just the models here. As a result, I can only really do a design review of what appears to be a fairly generic quiz application.

Right now your models are set up this way

Exam <-> Question <-> Answer

So exams can share questions, and questions can share answers. This is useful in cases where exams typically share questions, and questions share common answers, but it can (and often will) cause issues down the road. I'll get to those later.

Typically the next step is to build out the models for making selections on an exam. You might consider a adding a new many-to-many relationship on the exam that holds the selections (assuming a single exam per person). Here's a rough diagram of what that would look like, where the "selection" is the many-to-many relationship. This can either be implemented as a single property, or as a separate model with two foreign keys.

Selection -> Exam <-> Question <-> Answer <- Selection

But then there is a problem: How do you handle questions/answers duplicated on exams? There's no easy way to determine what selection tied to which question, because the answer that was selected is shared across multiple questions in the same exam. This means that you can't properly associate selections with questions, and you can't share answers across an exam, because there are multiple ways traverse the tree.

My recommendation would be to remove the flexibility of sharing questions and answers in favor of a more rigid, single-directional tree structure.

Exam <- Question <- Answer

So a question has a defined set of answers, and those answers are unique to the question and cannot be shared. And those questions are specific to the exam that are specified for, and cannot appear across multiple exams. This means that a selection (the "glue" between the answer and the exam) cannot be duplicated across a single exam, so there will always be a clear path from an exam to an answer, and the reverse.

Selection -> Exam <- Question <- Answer <- Selection

So the revised models would look like

from django.db import models


class Exam(models.Model):
    """
    Exam's model, works as a wrapper for the questions
    """
    name = models.CharField(max_length=64, verbose_name=u'Exam name', )
    slug = models.SlugField()

    def __str__(self):
        return self.name


class Question(models.Model):
    question_text = models.CharField(max_length=256, verbose_name=u'Question\'s text')
    is_published = models.BooleanField(default=False)
    exam = models.ForeignKey(Exam, related_name='questions')

    def __str__(self):
        return "{content} - {published}".format(content=self.question_text, published=self.is_published)


class Answer(models.Model):
    """
    Answer's Model, which is used as the answer in Question Model
    """
    text = models.CharField(max_length=128, verbose_name=u'Answer\'s text')
    is_valid = models.BooleanField(default=False)
    question = models.ForeignKey(Question, related_name='answers')

    def __str__(self):
        return self.text

All diagrams were created using nomnoml.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.