2

I am creating quiz app in django, my django model for questions is like this,

class Question(models.Model):
    questions = models.CharField(max_length=50, unique=True)
    choice1 = models.CharField(max_length=50, unique=True)
    choice2 = models.CharField(max_length=50, unique=True)
    choice3 = models.CharField(max_length=50, unique=True)
    choice4 = models.CharField(max_length=50, unique=True)
    correct_answer = models.CharField(max_length=50, unique=True)

is this fine or save the four options in postgres array or save the choices in separate table.

Sakthi Panneerselvam
  • 1,337
  • 1
  • 14
  • 25

4 Answers4

10

For a properly normalized relational database schema, you want a distinct Choice model with a foreign key on Question:

class Question(models.Model):
    question = models.CharField(...)

class Choice(models.Model):
    question = models.ForeignKey("Question", related_name="choices")
    choice = modelsCharField("Choice", max_length=50)
    position = models.IntegerField("position")

    class Meta:
        unique_together = [
            # no duplicated choice per question
            ("question", "choice"), 
            # no duplicated position per question 
            ("question", "position") 
        ]
        ordering = ("position",)

And then you can get at a Question's choices with myquestion.choices.all() (and get the question from a Choice with mychoice.question).

Note that this won't impose any limitation on the number of choices for a Question, not even mandates that a Question has at least one related Choice.

Unless you have a very compelling reason to do otherwise, a properly normalized schema is what you want when using a relational database (rdbms are much more than mere bitbuckets, they offer a lot of useful features - as long as you do have a proper schema, that is).

bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
  • how will save multiple choice in single field like postgres array and i need to save correct answer also – Sakthi Panneerselvam Dec 18 '17 at 11:58
  • 1
    You asked for recommandations on the best way to design your model. My recommandation is to use a properly normalized schema. If you want to use some denormalized schema instead please do, but I won't answer on this because I don't think this would really help. If you wonder why, try both solutions, and see which one makes your life easier when it comes to handling answers, scores, and just any stats on your questions/choices/answers. – bruno desthuilliers Dec 18 '17 at 12:03
  • Thank you. now only i get it what you're trying to say. – Sakthi Panneerselvam Dec 18 '17 at 12:25
0

This tutorial shows it all https://medium.com/@nsjcorps/create-a-quiz-application-with-django-rest-framework-react-redux-part-one-f0fcae5103fd

This is the summary of it though:

from django.db import models
from django.contrib.auth.models import User
from django.template.defaultfilters import slugify
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver


class Quiz(models.Model):
    name = models.CharField(max_length=1000)
    questions_count = models.IntegerField(default=0)
    description = models.CharField(max_length=70)
    created = models.DateTimeField(auto_now_add=True,null=True,blank=True)
    slug = models.SlugField()
    roll_out = models.BooleanField(default=False)

    class Meta:
        ordering = [‘created’,]
        verbose_name_plural =”Quizzes”

    def __str__(self):
        return self.name


class Question(models.Model):
    quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
    label = models.CharField(max_length=1000)
    order = models.IntegerField(default=0)

    def __str__(self):
        return self.label


class Answer(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    text = models.CharField(max_length=1000)
    is_correct = models.BooleanField(default=False)

    def __str__(self):
        return self.text


class QuizTakers(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
    correct_answers = models.IntegerField(default=0)
    completed = models.BooleanField(default=False)
    timestamp = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.user.username


class Response(models.Model):
    quiztaker = models.ForeignKey(QuizTakers, on_delete=models.CASCADE)
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    answer = models.ForeignKey(Answer,on_delete=models.CASCADE,null=True,blank=True)

    def __str__(self):
        return self.question.label

@receiver(post_save, sender=Quiz)
def set_default_quiz(sender, instance, created,**kwargs):
    quiz = Quiz.objects.filter(id = instance.id)
    quiz.update(questions_count=instance.question_set.filter(quiz=instance.pk).count())

@receiver(post_save, sender=Question)
def set_default(sender, instance, created,**kwargs):
    quiz = Quiz.objects.filter(id = instance.quiz.id)
    quiz.update(questions_count=instance.quiz.question_set.filter(quiz=instance.quiz.pk).count())

@receiver(pre_save, sender=Quiz)
def slugify_title(sender, instance, *args, **kwargs):
    instance.slug = slugify(instance.name)
onlyphantom
  • 8,606
  • 4
  • 44
  • 58
NduJay
  • 760
  • 1
  • 10
  • 23
0

you can use arryfield if you are using postgres sql

class QuizMcqDetail(BaseModel):
    title = models.CharField(max_length=255, null=True, blank=True)
    time_limit = models.TimeField()
    start_date = models.DateTimeField(null=True, blank=True)
    end_date = models.DateTimeField(null=True, blank=True)
    available_languages = models.ManyToManyField(QuizLanguage, related_name='available_language', blank=True)
    question_count = models.PositiveIntegerField(default=10)
    is_active = models.BooleanField(default=True)

    def __str__(self):
        return self.title

class QuizMcqQuestion(BaseModel):
    s_no = models.PositiveIntegerField()
    quiz = models.ForeignKey(QuizMcqDetail, on_delete=models.CASCADE, related_name='quiz_mcq')
    question_text = models.TextField(max_length=200)
    options = ArrayField(models.CharField(max_length=200))
    answer_position = ArrayField(models.IntegerField())
    explanation = models.TextField(null=True)
    language = models.ForeignKey(QuizLanguage, related_name='question_language', on_delete=models.SET_NULL, null=True)

    def __str__(self):
        return self.question_text

class QuizMcqSelect(BaseModel):
    quiz_question = models.ForeignKey(QuizMcqQuestion, related_name="select_quiz_mcq", on_delete=models.CASCADE)
    user = models.ForeignKey(User, related_name="user_select_quiz_mcq", on_delete=models.CASCADE)
    answer = models.BooleanField(default=False)
    selected_answer = ArrayField(models.IntegerField())

    def __str__(self):
        return f"{self.quiz_question} {self.user} {self.answer}"

    def save(self, *args, **kwargs):
        if self.quiz_question.answer_position == self.selected_answer:
            self.answer = True
        else:
            self.answer = False
        super().save(*args, **kwargs)
kkSkr
  • 125
  • 1
  • 6
steve harvey
  • 110
  • 7
0

The best way is:

ANSWER_CHOICES = (
    ("choice_1", "Answer_1"),
    ("choice_2", "Answer_2"),
    ("choice_3", "Answer_3"),
    ("choice_4", "Answer_4"),
    ("choice_5", "Answer_5"),
    ("choice_6", "Answer_6"),
   
)

class Question(models.Model):
    questions = models.CharField
               (
                max_length=50, 
                unique=True,
                choices=ANSWER_CHOICES
                )
    correct_answer = models.CharField(max_length=50, unique=True)
Manukumar
  • 235
  • 2
  • 3