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())
Related
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
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'".
This question is similar with others but it is a different one actually ! So, I have 3 models such as (I have deleted some unnecessary things for shorter code):
class Category(models.Model):
category_title = models.CharField(max_length=200)
category_content = models.TextField()
category_slug = models.CharField(max_length=200)
def __str__(self):
return self.category_title
class Classes(models.Model):
classes_title = models.CharField(max_length=200)
classes_content = models.TextField()
classes_category = models.ForeignKey(Category, on_delete=models.SET_DEFAULT)
def __str__(self):
return self.classes_title
class Subjects(models.Model):
subject_title = models.CharField(max_length=200)
subject_content = models.TextField()
subject_class = models.ForeignKey(Classes, on_delete=models.SET_DEFAULT)
def __str__(self):
return self.subject_title
So let me give an example. I can have 2 categories and in those categories I can have "same named" classes. Lets think about maths is a class for both categories. When I want to add a new subject to maths I see 2 same named maths in admin page. So I want to know which one belongs to which category in admin page. So I can add my subject to right class.
class SubjectAdmin(admin.ModelAdmin):
fields = ('subject_title', 'subject_content', 'subject_class',)
So in this picture (Subjects = Konular) I am adding a new subject. I will have a foreign key to Class. However I have same name for classes that are coming from different categories. So in this dropdown how can I know which class belongs to which category ?
Try this...
class KonularAdmin(admin.ModelAdmin):
fields = ('subject_title', 'subject_content', 'subject_class', 'get_classes_category_title')
def get_classes_category_title(self, obj):
subject_object = Subjects.objects.get(id=obj.subject_class)
return str(subject_object.classes_category.category_title)
It returns the category title name
If I understood you correctly, This should work.
class Classes(models.Model):
classes_title = models.CharField(max_length=200)
classes_content = models.TextField()
classes_category = models.ForeignKey(Category, on_delete=models.SET_DEFAULT)
def __str__(self):
return self.title
class Subjects(models.Model):
subject_title = models.CharField(max_length=200)
subject_content = models.TextField()
subject_class = models.ForeignKey(Classes, on_delete=models.SET_DEFAULT)
def __str__(self):
return f"{self.subject_title} - {str(self.subject_class)}"
You can use __str__() method to change the string representation of an object:
class Subjects(models.Model):
subject_title = models.CharField(max_length=200)
subject_content = models.TextField()
subject_class = models.ForeignKey(Classes, on_delete=models.SET_DEFAULT)
def __str__(self):
return f"{self.subject_title} - {self.subject_content} - {self.subject_class}"
# shorter version:
# return f"{self.subject_title[:10]} - {self.subject_content[:10]} - {self.subject_class[:10]}"
Check it with:
>>> print(Subjects.objects.first())
I'm trying out a new django social media project which has User, Status and Comment. The relationship between them is such that, User has a relationship to Status, User has relationship to Comment and there also exists a relationship between Status and comment.
So to achieve this I built four tables, i.e, User, Comment, Status and UCSModel. Where UCSModel is the table which has the foreign keys of the rest three tables. UCSModel is more like the relations table consisting of all ID's
So here is my models.py
from django.db import models
class User(models.Model):
username = models.CharField(max_length=50, unique=True)
email = models.CharField(max_length=200)
password = models.CharField()
def __str__(self):
return self.username
class Comment(models.Model):
comment = models.CharField()
def __str__(self):
return self.comment
class Status(models.Model):
status = models.CharField()
def __str__(self):
return self.status
class USCRelation(models.Model):
user = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
comment = models.ForeignKey(Comment, null=True, on_delete=models.CASCADE)
status = models.ForeignKey(Status, null=True, on_delete=models.CASCADE)
Being new to Django, I wanted to know if there is any better way of building relationships between tables. Especially when all the three tables are interrelated to each other like in my case above.
Find the below DB design, this would help you optimising your queries:
from django.db import models
class User(models.Model):
username = models.CharField(max_length=50, unique=True)
email = models.CharField(max_length=200)
password = models.CharField()
def __str__(self):
return self.username
class Status(models.Model):
status = models.CharField()
def __str__(self):
return self.status
class Comment(models.Model):
comment = models.CharField()
status = models.ForeignKey(Status, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.comment
Hope this helps!
I have the following models
class Company(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(
max_length=500,
blank=True,
help_text='Any text to describe a company'
)
url = models.URLField('company URL', blank=True, null=True)
email = models.EmailField(blank=True, null=True)
created_on = models.DateTimeField('date created', default=timezone.now)
class Meta:
verbose_name = 'company'
verbose_name_plural = 'companies'
ordering = ['name', '-created_on']
def __repr__(self):
return '<Company {0.name}>'.format(self)
def __str__(self):
return self.name
class Project(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(
max_length=500,
blank=True,
help_text='Any text to describe the project'
)
company = models.ForeignKey(
Company,
on_delete=models.PROTECT,
)
created_on = models.DateTimeField('date created', default=timezone.now)
class Meta:
verbose_name = 'project'
verbose_name_plural = 'projects'
ordering = ['-created_on', 'company']
permissions = (
("can_view_project",
"Can view all project related work"),
)
def __repr__(self):
return '<Project {0.name}>'.format(self)
def __str__(self):
return self.name
class Worker(models.Model):
description = models.TextField(
max_length=500,
blank=True,
help_text='Optional. Describe what the worker does or who they are'
)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
company = models.ForeignKey(Company)
class Meta:
order_with_respect_to = 'user'
def __repr__(self):
return '<Worker {0.id}'.format(self)
def __str__(self):
return self.user.get_fullname()
The problem
I would like to add a ManyToMany relationship between Project and Worker so that I can view
a list of workers under a certain project. However, I want to make sure that a worker can only
be added to a project if they are both part of the same company.
I was planning on using a junction table with a ForeignKey to both of their company attributes,
but according to the django docs, a foreignkey can only be used once per model
(https://docs.djangoproject.com/en/1.10/topics/db/models/#extra-fields-on-many-to-many-relationships)
How do I make sure that the many to many relationship between the two tables is limited to the same company?
Is there perhaps another way to ensure that workers cannot work on projects outside of their own company?
Assuming you define the many to many relationship this way in the Project model:
workers = ManyToManyField(Worker)
Assuming you have a model form named ProjectForm to create or modify projects. You can define a clean function in this form:
def clean(self):
cleaned_data = super(ProjectForm, self).clean()
for w in cleaned_data['workers']:
if w.company.id != cleaned_data['company'].id:
self.add_error('workers', your_error_message)
break
return cleaned_data
Hope this help.