In this quiz game, I'm trying to filter the questions of a particular course.
models.py
class Course(models.Model):
course_name = models.CharField(max_length=50)
date_created = models.DateTimeField(auto_now_add=True)
class Quiz(models.Model):
course_name = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='quiz_course')
question = models.TextField(max_length=100)
class Choice(models.Model):
question = models.ForeignKey(Quiz, on_delete=models.CASCADE, related_name="choice_question")
choice = models.CharField(max_length=100)
is_true = models.BooleanField("This is Correct Answer", default=False)
class Quizgame(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
total_score = models.DecimalField("Total score", default=0, decimal_places=2, max_digits=6)
start_time = models.DateTimeField(auto_now_add=True)
finish_time = models.DateTimeField(null=True)
def get_new_question(self,course_id):
used_question = AttemptedQuestion.objects.filter(quiz_profile=self).values_list('question__id',flat=True)
remaining_questions = Quiz.objects.exclude(id__in=used_question)
if not remaining_questions.exists():
return
return random.choice(remaining_questions)
def create_attempt(self, question):
attempted_question = AttemptedQuestion(question=question, quiz_profile=self)
attempted_question.save()
class AttemptedQuestion(models.Model):
quiz_profile = models.ForeignKey(Quizgame, on_delete=models.CASCADE, related_name='attempt')
question = models.ForeignKey(Quiz, on_delete=models.CASCADE)
choice = models.ForeignKey(Choice, on_delete=models.CASCADE, null=True)
is_true = models.BooleanField(default=False)
In above where in class Quizgame I filter the questions in get_new_question method here I passed an course_id as an argument by I don't know how to filter out with the course_id for the particular course questions..
If you are trying to link a question to a course, I can't see a ForeignKey related to a Course object in your AttemptedQuestion class. You should add
class AttemptedQuestion(models.Model):
# ...
course = models.ForeignKey(Course, on_delete=models.CASCADE, null=True)
and then you can simply filter by
qs = AttemptedQuestion.objects.filter(course=course_id)
Before answering your question there is a simple tip that will help you raise the readability of your code.
If you're creating FK to a model named Course, name the field course. "course_name" is implying we will get a name in that field.
To access all the Quiz instances by course_id variable, you can use the __ operator in filter() method like this:
Quiz.objects.filter(course_name__id=course_id)
Note the double underscore __ after the "course_name". What this does is telling Django:
"Filter Quiz where course_name's id is equal to 'course_id'".
Related
This is follow-up to a question to:
Filter Queries which sum of their amount field are greater or lesser than a number
which is supposed to be solved. Answer suggests using Window function with filter but this results in a error:
django.db.utils.NotSupportedError: Window is disallowed in the filter clause.
Comment from #atabak hooshangi suggests removing the Window function, but query doesn't work in intended way after that. Any ideas to solve this problem?
let's say we have these 2 models:
class Developer(models.Model):
first_name = models.CharField(max_length=40, null=False, blank=False,
unique=True)
last_name = models.CharField(max_length=40, null=False, blank=False,
unique=True)
profession = models.CharField(max_length=100, null=False)
cv = models.FileField(upload_to=upload_cv_location, null=True, blank=True)
description = models.TextField()
img = models.ImageField(upload_to=upload_location, null=True, blank=True)
class Meta:
verbose_name = 'Developer'
verbose_name_plural = 'Developers'
ordering = ('first_name',)
def __str__(self):
return f'{self.first_name} {self.last_name}'
class Skill(models.Model):
developer = models.ForeignKey(to=Developer, on_delete=models.CASCADE)
category = models.ForeignKey(to=SkillCategory, on_delete=models.SET_NULL,
null=True)
name = models.CharField(max_length=50, null=False)
priority = models.PositiveIntegerField()
class Meta:
ordering = ('-priority',)
def __str__(self):
return f'{self.name} skill of {self.developer}'
As you can see , we have a developer model which has relationship with skill. Each developer can have multiple skills.
now consider we want to get the developers whos sum of their priorities are greater than a number.
The orm query should work this way :
from django.db.models import Sum
developers = Developer.objects.annotate(tot=Sum('skill__priority')).filter(tot__gt=250).all()
The output will be the developers who has greater than 250 priority_sum .
You can filter tot which is an annotated variable in any way you want.
like .filter(tot__lte)
or
.filter(tot__lt)
I hope this is what you were looking for.
I want to display the child model data with the parent model data as well in a queryset.
This is my models in model.py
class Shareholders(models.Model):
sharehold_IC = models.CharField(primary_key=True, unique=True, validators=[RegexValidator(r'^\d{12,12}$'), only_int], max_length=12)
sharehold_name = models.CharField(max_length=100, null=True)
sharehold_email = models.CharField(max_length=50, null=True, unique=True)
sharehold_address = models.TextField(null=True, blank=True)
password = models.CharField(max_length=100)
def __str__(self):
return self.sharehold_name
class Meeting(models.Model):
MEETING_STATUS = (
('Coming Soon', 'Coming Soon'),
('Live', 'Live'),
('Closed', 'Closed')
)
meeting_ID = models.CharField(primary_key=True, max_length=6, validators=[RegexValidator(r'^\d{6,6}$')])
meeting_title = models.CharField(max_length=400, null=True)
meeting_date = models.DateField()
meeting_time = models.TimeField()
meeting_desc = models.CharField(max_length=500, null=True)
meeting_status = models.CharField(max_length=200, null=True, choices=MEETING_STATUS)
date_created = models.DateTimeField(default=timezone.now, null=True)
def __str__(self):
return self.meeting_ID
class Question(models.Model):
question_ID = models.AutoField(primary_key=True)
question = models.CharField(max_length=400, null=True)
meeting_id = models.ForeignKey(Meeting, on_delete=CASCADE)
shareholder_IC = models.ForeignKey(Shareholders_Meeting, on_delete=CASCADE, related_name='shareholder_ic')
date_created = models.DateTimeField(default=timezone.now, null=True)
def __str__(self):
return str(self.meeting_id)
I try to display all the data from the Question model with the shareholders details such as sharehold_name from the Shareholders model.
This is how I try to do in Views.py.
Views.py
def getMessages(response, meeting_id):
meeting = Meeting.objects.get(meeting_ID=meeting_id)
questions = Question.objects.filter(meeting_id=meeting.meeting_ID)
# print(list(questions.values('shareholder_IC_id')))
for i in questions:
print(i.shareholder_ic.all())
return JsonResponse({"questions":list(questions.values())})
But somehow I got this error AttributeError: 'Question' object has no attribute 'shareholder_ic'.
I want to get the result like this:
{'question_ID': 141, 'question': "I'm good. How are you?", 'meeting_id_id': '731404', 'shareholder_IC_id': '122311221231', 'date_created': datetime.datetime(2021, 10, 7, 3, 40, 12, 160029, tzinfo=<UTC>), 'sharehold_name':'John Steve', 'sharehold_email':'john#gmail.com'}
How do I fix this or is there any other way to display the data?
Thanks in advance
To begin with, you're not using the related_name attribute in your Question model correctly. It's not to use an alternative name to refer to the related model, the opposite, it's the name you want to use to refer to the Question model from the related model, Shareholders in your case. You have to remake your model:
class Question(models.Model):
question_ID = models.AutoField(primary_key=True)
question = models.CharField(max_length=400, null=True)
meeting_id = models.ForeignKey(Meeting, on_delete=CASCADE)
shareholder_IC = models.ForeignKey(Shareholders_Meeting, on_delete=CASCADE, related_name='questions')
date_created = models.DateTimeField(default=timezone.now, null=True)
def __str__(self):
return str(self.meeting_id)
Then, in your views.py you need to use the correct attribute name in the Question model, which is shareholders_IC:
def getMessages(request, meeting_id): # <- Shouldn't the first param be request?
meeting = Meeting.objects.get(meeting_ID=meeting_id)
# No need to use meeting.meeting_ID here, you can use the meeting instance
questions = Question.objects.filter(meeting_id=meeting)
for i in questions:
print(i.shareholder_IC) # <- No .all(), a question has only one shareholder
return JsonResponse({"questions":list(questions.values())})
However, to achieve what you want, you should either use Django REST Framework, or serialize yourself the different instances that you'll get:
def getMessages(request, meeting_id):
meeting = Meeting.objects.get(meeting_ID=meeting_id)
questions = Question.objects.filter(meeting_id=meeting)
questions_list = []
for i in questions:
questions_list.append(
{
"question_ID": i.question_ID,
"question": i.question,
"shareholder_IC_id": i.shareholder_IC.shareholder_IC,
...
}
)
return JsonResponse(questions_list)
I would recommend two main things:
Learn about relationships in Django and how to represent them in your models. You shouldn't have an attribute meeting_id, for example, in your Question. Although internally the id is indeed being used to query the Meeting, the attribute has a relationship to the model, not to the ID.
Use DRF. It will really help you a lot.
You must use backward relations
questions = Question.shareholder_ic.filter(meeting_id=meeting.meeting_ID)
Do not touch it on the object
For other settings, create the required query set in manager
My group project for school has us building a school management system. I have the following models:
Student:
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
User.is_student = True
enrolled_courses = models.ManyToManyField(Session, blank=True)
def __str__(self):
return f'{self.user.last_name}, {self.user.first_name}'
Session:
class Session(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE)
course_date_start = models.DateTimeField()
course_date_end = models.DateTimeField()
def session_id(self):
new_session_date = self.course_date_start.strftime('%Y')
return f'{new_session_date}{self.course.number}{self.pk}'
def __str__(self):
return f'{self.course.number} - {self.course.title} - {self.session_id()}'
Assignment:
class Assignment(models.Model):
session_link = models.ForeignKey(Session, on_delete=models.CASCADE)
created_date = models.DateTimeField(default=timezone.now)
due_date = models.DateField()
title = models.CharField(max_length=50)
total_points = models.IntegerField()
points_earned = models.IntegerField(blank=True, null=True)
objective = models.TextField()
def __str__(self):
return self.title
The problem right now is if I save one value of points_earned to a user, it saves that value to all, since they're linked by the FK.
What's the best way to handle it so each Student can have their own score for each assignment?
If you want each Student to have their own score for each assignment,then a solution would be to have a table to keep track of the score with those two models as foreign keys.
Use https://docs.djangoproject.com/en/3.1/topics/db/models/#intermediary-manytomany
class StudentAssignment(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE)
assignment = models.ForeignKey(Assignment, on_delete=models.CASCADE)
points_earned = models.IntegerField(blank=True, null=True)
I have some models:
from django.db import models
from django.conf import settings
class Question(models.Model):
name = models.CharField(unique=True, max_length=50)
desctiption = models.TextField(max_length=500, unique=True)
def __repr__(self):
return f"Question: {self.name}"
class Answer(models.Model):
"""Answer for one question. Can be correct or incorrect"""
name = models.CharField(unique=True, max_length=50)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
answer = models.CharField(max_length=50)
is_correct = models.BooleanField(default=False)
def __repr__(self):
return f"Answer: {self.name}"
class Quiz(models.Model):
"""Quiz Model contain questions"""
name = models.CharField(unique=True, max_length=50)
desctiption = models.TextField(max_length=500, unique=True)
questions = models.ManyToManyField(Question)
def __repr__(self):
return f"Quiz: {self.name}"
class ProgressUserQuiz(models.Model):
"""Save user progress for any quiz"""
...
I can't understand how to structure ProgressUserQuiz.
For example. If user passed two questions in quiz and close a tab he can return to his last questions. And When he finished I want to add this in model.
How can I make it?tnx
You can use a Status flag for the questions
class ProgressUserQuiz(models.Model):
status_opt = (
('answered',"Answered"),
('passed',"Passed"),
('closed',"Closed"),
)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
status = models.CharField(choices=status_opts,max_length=50)
something else like answer and marks?...
You can update this table after each question is done, and use this model to get the status. You can always Filter the data for questions of some quiz from these tables as well.
ProgressUserQuiz.objects.filter(question__in=list(quizinstance.questions.all())
I am really stuck on this error and it does not make sense why it does not follow the relationship backward on Registered_Courses on the foreign key for Courses when i use course_set
views.py
def registered_coursesView(request, username):
'''Page to display the registered courses of a user.'''
registeredCourses = Registered_Courses.objects.get(owner = request.user)
courseInfo = registeredCourses.course_set.all()
context = {'registeredCourses': registeredCourses, 'courseInfo':courseInfo}
return render(request, 'safetyCourseApp/registered_courses.html', context)
models.py
class Course(models.Model):
'''Offered Course information.'''
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
name = models.CharField(max_length=200, primary_key=True)
description = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
start_date = models.DateField()
end_date = models.DateField()
price = models.DecimalField(max_digits=10, decimal_places=2)
capacity = models.IntegerField()
registered_ppl = models.IntegerField()
def __str__(self):
"""Return a string representation of the model."""
return self.name
class Registered_Courses(models.Model):
"""Something specific learned about a Course."""
registered_course = models.ForeignKey(Course, on_delete=models.CASCADE, null=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
def __str__(self):
"""Return a string representation of the model."""
return f'{self.owner}'
Please let me know what you guys think. I cannot think of a reason why this is not working. Thanks!
As you have specified in your models, each Registered_Courses will have a FK to Course, So each Course can have multiple Registered_Courses.
But you are trying to get multiple Course objects from a single Registered_Courses
The backward relationship is something like:
>>> course = Course.objects.first()
>>> course.registered_courses_set