Perform JOIN in tables django - python

Perform JOIN on in django fetching related date in reverse relationship.
There are three models.
Following is code for models
class Question(models.Model):
title = models.CharField(max_length=255 )
description = models.TextField(max_length=300)
class Quiz(models.Model):
name = models.CharField(max_length=225,blank=False )
quiz_type =models.IntegerField(choices=QUIZ_TYPE,default=0)
questions = models.ManyToManyField( Question, through='QuestionQuiz', related_name="quiz_question")
categories= models.ManyToManyField(Category,through='CategoryQuiz',related_name='quiz_category')
class QuestionQuiz(models.Model):
quiz = models.ForeignKey(Quiz,on_delete=models.CASCADE)
question = models.ForeignKey(Question,on_delete=models.CASCADE)
correct =models.DecimalField(max_digits=4, decimal_places=3)
incorrect= models.DecimalField(max_digits=4, decimal_places=3)
class Meta:
unique_together = ('quiz','question')
In this the questions are added to the quiz using model Question Quiz.
Here , QuizQuestion has foreign key relationship with the question. I need to fetch all from question JOIN and records from QuestionQuiz with a particular quiz_id.
Suppose quiz_id =3
Then I will fetch all questions with correct and incorrect. if that quiz id is added to the question then it will display correct incorrect else these would be blank.
question_id | title|description|correct|incorrect|quesquizid
1 | Q1 |Q1desc |2 | -2 | 1
2 | Q2 |Q2desc | | |
ques_id =1 added to quiz_id=3 .ques_id=2 not added to quiz_id=3.So, correct incorrect are blank.
I tried following but it fetches all question and related quizes scores irrespective of their occurrence in current quiz :
Question.objects.prefetch_related('questionquiz_set').all()
The result should be similar to following query
Select * from question as qs LEFT JOIN questionquiz as qq on (qq.question_id = qs.id AND qq.id=3)
Please check the result of the query:

I think prefetch_related along with Prefetch may get you the desired result.
q = Question.objects.prefetch_related(
Prefetch('questionquiz_set', queryset=QuestionQuiz.objects.filter(quiz=3))
)
print(q)
This may retrieve all related data along with null if exist.

Related

Transforming SQL Query to Django Expression

Assuming I have the following Django models and the according SQL-Tables with some data. I have simplified the models so that it is clearer.
UserAnswer:
class UserAnswer(models.Model):
partquestion = models.ForeignKey(PartQuestion)
answer = models.CharField()
id
answer
partquestion_id
1
667
1
PartQuestion:
class PartQuestion(models.Model):
question = models.ForeignKey(Question)
part = models.ForeignKey(Part)
id
question_id
part_id
1
1
1
Solution:
class SingleInputSolution(models.Model):
question = models.ForeignKey(Question)
content = models.CharField()
id
content
question_id
1
667
1
2
85
2
I want to get all User answers where the answer is equal to the solution of the according question. I've came up with this SQL-Query but can't really think of how to implement this into a Djano query:
select * from (useranswer join partquestion on
useranswer.partquestion_id = partquestion.id) join solution on
partquestion.question_id = solution.question_id where answer=content;
This will output the following:
useranswer.id
useranswer.answer
useranswer.partquestion_id
partquestion.id
partquestion.question_id
partquestion.part_id
solution.id
solution.content
solution.question_id
1
667
1
1
1
1
1
667
1
I just can't get my head around this concept in Django. Maybe using F-Expressions and some stuff.
Thanks for any advice.
You can just use values and the __ relations to traverse the foreign keys.
UserAnswer.objects.values('id', 'answer', 'partquestion_id', 'partquestion_set__id', 'partquestion_set__question_id')
Unfortunately it isn't obvious enough to me how to get those columns for the last table, but I hope that from this answer you can see how to do it?
Okay well I just figured it out myself after reading the answer and comments from #Swift.
It's:
UserAnswer.objects.filter(partquestion__question__solution__content=F('answer'))
It's pretty simple now after I got it but I just tried using solution_set instead of just solution.
If you define your models like this:
class UserAnswer(models.Model):
question = models.ForeignKey(questions,on_delete=models.CASCADE)
user_answer = models.CharField()
class questions(models.Model):
question = models.CharField()
answer = models.CharField()
then this filter must work
right_answers = UserAnswer.objects.filter(question__answer = user_answer )

Django ORM: Joining a table to itself

I have a table with cryptocurrency prices:
id | price | pair_id | exchange_id | date
---+--------+---------+-------------+---------------------------
1 | 8232.7 | 1 | 1 | 2018-02-09 09:31:00.160837
2 | 8523.8 | 1 | 2 | 2018-02-09 09:31:01.353998
3 | 240.45 | 2 | 1 | 2018-02-09 09:31:02.524333
I want to get the latest prices of a single pair from different exchanges. In raw SQL, I do it like this:
SELECT b.price, b.date, k.price, k.date, AVG((b.price + k.price) / 2)
FROM converter_price b JOIN converter_price k
WHERE b.exchange_id=1 AND k.exchange_id=2 AND b.pair_id=1 AND k.pair_id=1
ORDER BY b.date DESC, k.date DESC LIMIT 1;
8320.1|2018-02-09 11:23:00.369810|8318.2|2018-02-09 11:23:06.467424|8245.05199328066
How to do such query in the Django ORM?
EDIT: Adding models.py. I understand that it's quite likely that I'll need to update it.
from django.db import models
# Create your models here.
class Pair(models.Model):
identifier = models.CharField('Pair identifier', max_length=6)
def __str__(self):
return self.identifier
class Exchange(models.Model):
name = models.CharField('Exchange name', max_length=20)
def __str__(self):
return self.name
class Price(models.Model):
pair = models.ForeignKey(Pair, on_delete=models.CASCADE)
exchange = models.ForeignKey(Exchange, on_delete=models.CASCADE)
price = models.FloatField(default=0)
date = models.DateTimeField(auto_now=True, db_index=True)
def __str__(self):
return '{} - {}: {} ({})'.format(
self.date, self.pair, self.price, self.exchange)
EDIT2: To clarify what I'm really after.
I want the latest price with pair_id=1 and exchange_id=1, and the latest price with pair_id=1 and exchange_id=2. With a single query, and without any subsequent processing in Python - of course I can get Price.objects.all() and then search for it myself, but that's not the right way to use the ORM.
The way to do this with raw SQL is joining the table to itself. Showing how to join a table to itself using the Django ORM would (probably) answer the question.

How to join tables with multiple foreign keys and get filtered data in Django?

I have two models like this:
class Question(models.Model):
ques_id = models.IntegerField()
test_id = models.ForeignKey('exam.Test')
ques = models.TextField()
class UserAnswer(models.Model):
user = models.ForeignKey('exam.User')
test_id = models.ForeignKey('exam.Test')
ques_id=models.ForeignKey('exam.Question')
user_ans = models.TextField()
I need to execute this query to get the correct 'ques' field values.
SELECT A.ques_id, B.ques, A.user_ans FROM useranswer A
inner join question B on B.ques_id= A.ques_id and B.test_id =A.test_id
WHERE A.user_id=1 and B.test_id='101'
So far what I have done:
UserAnswer.objects.filter(test_id=test_id, user_id=user_id).values('ques_id', 'ques_id__ques','user_ans')
But it doesn't returning the right 'ques' field values because it doesn't considering the B.test_id =A.test_id section. How to retrieve it???
First of all, your field names are misleading. Do not
suffix foreign key fields with _id! Accessing them as attributes returns model instances and django provides the _id suffixed attributes to access the actual keys implicitly:
class Question(models.Model):
test = models.ForeignKey('exam.Test')
ques = models.TextField()
# is this supposed to be the primary key? Don't do that
# ques_id = models.IntegerField()
class UserAnswer(models.Model):
user = models.ForeignKey('exam.User')
ques = models.ForeignKey('exam.Question')
user_ans = models.TextField()
# This is redundant as ques belongs to a test already
# test = models.ForeignKey('exam.Test')
I assume you want to get all the answers for one user and one test:
UserAnswer.objects.filter(
ques__test_id=test_id,
user_id=user_id
).values('ques_id', 'ques__ques', 'user_ans')

How can I make a query by a Django foreignkey description?

if i have this code
class Father(models.Model):
Description = models.CharField(max_length=50)
Code = models.IntegerField(max_length=2, unique=True)
def __unicode__(self):
return "%s (%s)" % (self.Description, self.Code)
class Son(models.Model):
Father = models.ForeignKey(Father)
Description = models.CharField(max_length=50)
And I want to create a filter by the select box shown to the user.
Assuming that I have a record in Father like:
# | Description | Code
--------------------------
1 | Fred Nichols | 100
In the ForeignKey field should have a HTML content like:
<option value="1">Fred NIchols (100)</option>
if I try to query by the field in the son model:
Son.objects.filter(Father__Contains="Fred")
I get an error. In the documentation of Django the nearest option should be something like:
Son.objects.filter(Father__Description__contains="Fred")
or for any of the columns data
Son.objects.filter(Q(Father__Description__contains="Fred") | Q(Father__Code__Contains="Fred") )
I'd like to make something the most similar to the user info shown.
How could I make a query that could do that?
In single step u could do this :
Son.objects.filter(father__Description__contains="Fred")
you were doing it right the only thing that was wrong is :
the referencing entity Father should be lowercase name of the model
reference

Django: query with ManyToManyField count?

In Django, how do I construct a COUNT query for a ManyToManyField?
My models are as follows, and I want to get all the people whose name starts with A and who are the lord or overlord of at least one Place, and order the results by name.
class Manor(models.Model):
lord = models.ManyToManyField(Person, null=True, related_name="lord")
overlord = models.ManyToManyField(Person, null=True, related_name="overlord")
class Person(models.Model):
name = models.CharField(max_length=100)
So my query should look something like this... but how do I construct the third line?
people = Person.objects.filter(
Q(name__istartswith='a'),
Q(lord.count > 0) | Q(overlord.count > 0) # pseudocode
).order_by('name'))
Actually it's not the count you're interested in here, but just whether or not there are any members in that relationship.
Q(lord__isnull=False) | Q(overlord__isnull=False)
In this case, better resort to raw SQL.
for p in Person.objects.raw('SELECT * FROM myapp_person WHERE...'):
print p

Categories

Resources