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.
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).
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)
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)
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)
Related
I am working on a pool app and want to add the total_likes attribute to the model which will allow me to count the number of users who liked the question, just like Youtube community question allows.
I just tried to override the save(*args, **kwargs) but got many errors.
What should I do now?
from django.db import models
from django.contrib.auth.models import User
class Question(models.Model):
text = models.TextField()
voters = models.ManyToManyField(to=User, blank=True, related_name='voters')
impression = models.CharField(max_length=30, choices=impression_choices, blank=True, null=True)
date_created = models.DateTimeField(auto_now_add=True)
likes = models.ManyToManyField(to=User, related_name='likes', blank=True)
total_likes = models.IntegerField(default=0)
def __str__(self):
return self.text
def save(self, *args, **kwargs):
self.total_likes = self.likes_set.count()
super().save(*args, **kwargs)
The Model below works totally fine. In the model above I tried to override the save() method but I don't know how?
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
text = models.CharField(max_length=20)
votes = models.IntegerField(blank=True, default=0)
def __str__(self):
return self.text
I am trying to get the objects from a many to many field. when the user selects their answer I want to be able to get the associated objects from the MtoM field.Then increment the related objects ansData + 1. ans in models was something else but i changed it for reasons, but that is what I am tryning to increment.
models.py
class User(models.Model):
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=25)
#password = models.CharField(max_length=25)
email = models.EmailField(max_length=100)
class Quiz(models.Model):
name = models.CharField(max_length=200,primary_key=True)
NOQ = models.IntegerField(default=1)
class Meta:
verbose_name = "Quiz"
verbose_name_plural = "Quizzes"
def __str__(self):
return self.name
#number Of Questions
class Major(models.Model):
major = models.CharField(max_length=200)
ans = models.IntegerField(default=0)
answer = models.ManyToManyField('Answer')
def __str__(self):
return self.major
class Question(models.Model):
question_text = models.CharField(max_length=400)
quiz = models.ForeignKey("Quiz", on_delete=models.CASCADE, null=True)
def __str__(self):
return self.question_text
class Answer(models.Model):
question = models.ForeignKey('Question', on_delete=models.CASCADE, null=True)
answer_text = models.CharField(max_length=200)
def __str__(self):
return self.answer_text
class QuizTaker(models.Model):
user = models.ForeignKey("User", on_delete=models.CASCADE)
quiz = models.ForeignKey("Quiz", on_delete=models.CASCADE)
completed = models.BooleanField(default=False)
def __str__(self):
return self.user
views.py
class QuizView(DetailView):
model = Question
template_name = 'Quizzes/quiz.html'
class ResultsView(generic.DetailView):
model = Question
template_name = 'JSUMA/results.html'
def vote(request, question_id):
question = get_object_or_404(Question, question_id)
try:
selected_answer = question.answer_set.get(pk=request.POST['answer'])
except (KeyError,Answer.DoesNotExist):
return render(request, 'Quizzes/quiz.html,' {'question' : question,
'error_message' : "You didn't select an answer.",})
else:
This is very similar to the Official Django Tutorial showing the Polls App. I recommend you to once view it carefully.
Rest, you should add a number of votes integer field to every answer.
selected_answer = Answer.objects.get(pk=request.POST['answer'])
Now simply increase vote of this answer.
class Poll(models.Model):
question = models.CharField(max_length=200)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
pub_date = models.DateTimeField(auto_now=True)
expire_date = models.DateTimeField(blank=True, null=True)
slug = models.SlugField(max_length=100)
def _get_unique_slug(self):
slug = slugify(self.question)
unique_slug = slug
num = 1
while Poll.objects.filter(slug=unique_slug).exists():
unique_slug = '{}-{}'.format(slug, num)
num += 1
return unique_slug
def save(self, *args, **kwargs):
self.slug = self._get_unique_slug()
super().save(*args, **kwargs)
def __str__(self):
return self.question
class Meta:
ordering = ('-pub_date',)
class Choices(models.Model):
poll = models.OneToOneField(Poll, related_name='choices')
choice_text = models.CharField(max_length=200)
voter = models.OneToOneField(User, on_delete=models.CASCADE, related_name='voters', blank=True, null=True)
vote_count = models.PositiveIntegerField(default=0)
def __str__(self):
return self.choice_text
def save(self, *args, **kwargs):
self.vote_count += 1
super().save(*args, **kwargs)
class Meta:
pass # order_with_respect_to = 'poll'
It doesn't allow user to vote more than one poll. and if I change it to voter = models.Foriegnkey(User, on_delete=models.CASCADE, related_name='voters', blank=True, null=True)
It allows users to vote more than a single option in a poll which is not the intended behaviour.
I will appreciate if I can't get help to fix this. Thank you.
I want to have a form which only offers the user to post a question for a project he is participating in.
models.py:
class Project(models.Model):
project_name = models.CharField(max_length=255, unique=True, blank=False)
def __str__(self):
return str(self.project_name)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
project = models.ManyToManyField(Project)
def __str__(self):
return str(self.user)
class Question(models.Model):
title = models.CharField(max_length=255, blank=False)
content = tinymce_models.HTMLField(blank=False)
author = models.ForeignKey(User, on_delete=models.CASCADE)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
...
def __str__(self):
return str(self.title)
class QuestionForm(ModelForm):
class Meta:
model = Question
fields = ['title', 'content', 'project']
in views.py:
form = QuestionForm()
form.fields["project"].queryset = Project.objects.filter(project_name__in=request.user.profile.project.all())
But somehow the result of the query always stays empty.
Does somebody maybe have an idea what I am missing?
Your query is over complicated. You should just use the user's projects directly:
form.fields["project"].queryset = request.user.profile.project.all())
i have the following models setup
class Player(models.Model):
#slug = models.slugField(max_length=200)
Player_Name = models.CharField(max_length=100)
Nick = models.CharField(max_length=100, blank=True)
Jersy_Number = models.IntegerField()
Team_id = models.ForeignKey('Team')
Postion_Choices = (
('M', 'Manager'),
('P', 'Player'),
)
Poistion = models.CharField(max_length=1, blank=True, choices =Postion_Choices)
Red_card = models.IntegerField( blank=True, null=True)
Yellow_card = models.IntegerField(blank=True, null=True)
Points = models.IntegerField(blank=True, null=True)
#Pic = models.ImageField(upload_to=path/for/upload, height_field=height, width_field=width, max_length=100)
class PlayerAdmin(admin.ModelAdmin):
list_display = ('Player_Name',)
search_fields = ['Player_Name',]
admin.site.register(Player, PlayerAdmin)
class Team(models.Model):
"""Model docstring"""
#slug = models.slugField(max_length=200)
Team_Name = models.CharField(max_length=100,)
College = models.CharField(max_length=100,)
Win = models.IntegerField(blank=True, null=True)
Loss = models.IntegerField(blank=True, null=True)
Draw = models.IntegerField(blank=True, null=True)
#logo = models.ImageField(upload_to=path/for/upload, height_field=height, width_field=width, max_length=100)
class Meta:
pass
#def __unicode__(self):
# return Team_Name
#def save(self, force_insert=False, force_update=False):
# pass
#models.permalink
def get_absolute_url(self):
return ('view_or_url_name')
class TeamAdmin(admin.ModelAdmin):
list_display = ('Team_Name',)
search_fields = ['Team_Name',]
admin.site.register(Team, TeamAdmin)
my question is how do i get to the admin site to show Team_name in the add player form Team_ID field currently it is only showing up as Team object in the combo box
You are almost there, you have commented it out and forgot to call the attribute properly:
def __unicode__(self):
return self.Team_Name
Read the documentation.
And for updated developers (Python 3.x):
def __str__(self):
return self.Team_name
Add a unicode method to the team object:
def __unicode__(self):
return self.Team_name