Django: implementing JOIN using Django ORM? - django

I have a Q&A type of site built in Django with the following models:
class Question(models.Model):
title = models.CharField(max_length=70)
details = models.TextField()
class Answer(models.Model):
question_id = IntegerField()
details = models.TextField()
I need to display a specific question together with its answers. Normally I'd need 2 queries to do that:
Question.objects.get(id=1)
Answer.objects.get(question_id=1)[:10]
I'm hoping to retrieve everything using one query. In MySQL it'd be:
SELECT *
FROM Question JOIN Answer ON Question.id=Answer.question_id
WHERE Question.id=1
LIMIT 10
Is there anyway I could do this through Django's ORM?
Would extra() help in this case?

This is exactly what select_related() does. The only gotcha is that
you have to start with the Answer model, rather than Question, but the
result is the same:
answers = Answer.objects.filter(question_id=1).select_related()
Now each answer object has a pre-fetched 'question' attribute, and
accessing it won't hit the db again.

Consider using models.ForeignKey(Question) instead of question_id = IntegerField().
This is the optimal (more relational) way to express the relationship between Questions and Answers you are trying to portray.
This way you can simply call Answers.objects.filter(question_id=<id>) and get exactly what you're looking for.

class Question(models.Model):
title = models.CharField(max_length=70)
details = models.TextField()
class Answer(models.Model):
question = models.ForeignKey('Question')
details = models.TextField()
id = <whatever_id>
answers = Question.objects.get(id=id).answer_set.all()

#Consider A Foreign Key Relationship Between Books And Publisher
class Publisher(models.Model):
name = models.CharField(max_length=100)
eclass Book(models.Model):
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
#Fetch Publisher Name For A Book
book = Book.objects.select_related('publisher').get(id=1)
book.publisher.name
#Fetch books which have specific publisher
publisher = Publisher.objects.prefetch_related('book_set').get(id=1)
books = publisher.book_set.all()
for more
https://kwikl3arn.com/django/JOINS

Related

django model reference, Add more contraint to reference

I have a question about Django model references.
I want to add more constraints to a ForeignKey or ManytoManyField like this:
question_id = models.ManyToManyField(Question(open==True))
Or put another similar constraint:
Actually I don't want to show a question that is not open as a selection( heightened in Answer form like the image above),
of course, I have done it by some query but does Django has any built-in support for it? I have tried some other way but it didn't work. Thanks!
class Answer(models.Model):
"""Give answers"""
answer = models.TextField()
question_id = models.ManyToManyField(Question(open==True))
upVote = models.IntegerField(default=0)
downVote = models.IntegerField(default=0)
def __str__(self):
"""return string """
return self.answer
While you still can, change your model relations. What you have now is that an answer can be linked to multiple questions. However, answer contains up and down vote.
So I can create two questions:
Is 1 + 1 two?
yes
no
Is 1 + 1 three?
yes
no
I can link the answers "yes" and "no" to both questions, which can be convenient with a good interface and is normalized. But they will share the up/down votes. Instead, answer should have a foreign key to question, because an answer can only be linked to one question at a time, even if the answer text is identical to prevent sharing of the up and down votes.
Secondly, we generally don't name fields question_id, but question:
From an object relation perspective, you relate an answer to a question not to a question id. (Under the hood, question_id is created to allow faster lookups and to serve as field name in the underlying database table).
On to your actual problem: you want to limit the available choices, which is what limit_choices_to does for you. So you would end up with this:
class Answer(models.Model):
"""Give answers"""
answer = models.TextField()
question = models.ForeignKey(
Question, on_delete=models.CASCADE, related_name='answers',
limit_choices_to={'open': True},
)
upVote = models.IntegerField(default=0)
downVote = models.IntegerField(default=0)
def __str__(self):
"""return string """
return self.answer
# Serializer
class AnswerSerializer(serializers.ModelSerializer)
class Meta:
model = Answer
fields = ('answer', 'upVote', 'downVote', 'question_id')
As you can see, your serializer can reference the magic field question_id.

When I use model in django, and I use many to one, How do I get the top 5 by comparing the count of the many

this is my models:
class question(models.Model):
question_user_id = models.ForeignKey('log_in.user', on_delete=models.CASCADE)
question_type = models.CharField(max_length=10)
question_date = models.DateTimeField('date published')
question_name = models.CharField(max_length=100)
question_content = models.CharField(max_length=10000)
class comment(models.Model):
comment_question_id = models.ForeignKey(question, on_delete=models.CASCADE)
comment_date = models.DateTimeField('date published')
comment_content = models.CharField(max_length=5000)
comment_user = models.ForeignKey('log_in.user',on_delete=models.CASCADE)
we can know that comment and question is in a relationship of many to one, I wonder how can i get the top 5 questions which has the most comments, I know I can find all the questions and use some arithmetics to solve it, but it costs a lot, can I just use the django model's Built-in methods to solve the problem?
maybe like use question.objects.order_by(*),(actually, I don't know what to write in the *)
I think you can achieve it by this:
from django.db.models import Count
Question.objects.filter(question_user_id=user_object).annotate(ccount=Count('comment').order_by('-ccount')[:5]
FYI: in model defination, please don't use something_id as field name, because underneath, django creates a table with name something_id if the table's name is something. And the class name should be Pascal Case according to pep8 style guide.

How to add multiple form field types of choices for a question in django?

I'm creating a "Polls" like application. In this application there will be questions and every question will have single or multiple answers choices. The answer choice can be Text, Image, or Video.
I have made one model called Question and different models for different choice types:
class Question(models.Model):
question_text = models.TextField()
category = models.ForeignKey(QuestionCategory)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class ChoiceText(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice = models.CharField(max_length=255)
class ChoiceImage(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice = models.ImageField()
Is this a good way to design the models, or is there a better way to solve this problem ?
It doesn't look so intuitive in the admin side too, any suggestions on the admin side ? I looked at https://docs.djangoproject.com/en/2.1/intro/tutorial07/ but I'm still confused about combining the different choices into a better user-friendly way.
I suggest you to go with same model (Choice). You can have a field named choice_type which selects the type of choice ('image','text','video').
class Choice(models.Model):
answer_type_choices = (
('1','Text'),
('2','Image'),
('3','Video'),
)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_type = model.CharField(max_length=1,choices = answer_type_choices)
choice = models.CharField(max_length=255)
choice_media = models.FileField(upload_to='uploads/')
And while adding the data from admin or front you can have form with some customization that has validation rules in clean method ( require fields based on choice type, image field validation, video file validation ) based on choice_type.
This is probably the best way to do it, assuming a question could have multiple answers of the same type, e.g. one question might have 4 text choices and 2 image choices.
Depending on how you plan on using these models, you may want to create a model class called Choice and have the other models subclass from it.
Django's admin form is highly customizable. Since you haven't given us any specifics, feel free to explore the Django admin docs.

Django-views Custom filter

If having different books stored in django database, each book has a date in which it was added to the database. Is their a way of filtering books written by a certain author that was within a date range only using django views?
Not sure what you mean by only django views, I assume you want to use querysets. Your question is poorly written - read this.
class Book(models.Model):
title = models.CharField(max_length=200)
date = models.DateTimeField()
author = models.ForeignKey(Author)
class Author(models.Model):
name = models.CharField(max_length=200)
And Queryset would be something like this.
books = Book.objects.filter(author__name=authors_name,
date__range=["2011-01-01", "2011-01-31"])

How can I eagerly load an answers latest revision?

I have a Q&A site and it allows answers to be changed while storing a revision of each change, much like here on Stackoverflow. When selecting some answers, I'd like it to include the most recent(but not all) revision as a property of each answer, so that I can avoid doing n+1 queries on my page.
class Answer(models.Model):
user = models.ForeignKey(User)
problem = models.ForeignKey(Problem)
class AnswerRevision(models.Model):
answer = models.ForeignKey(Answer, related_name='revisions')
text = models.TextField()
timestamp = models.DateTimeField()
Answer.objects.filter(problem=p)
I'd suggest the following structure:
class Answer(models.Model):
user = models.ForeignKey(User)
revision = models.ForeignKey(AnswerRevision)
problem = models.ForeignKey(Problem)
class AnswerRevision(models.Model):
answer = models.ForeignKey(Answer, related_name='revisions')
text = models.TextField()
timestamp = models.DateTimeField()
The only difference is that I added a revision property to the answer. When a new revision is made, just change the FK to point to the new revision.