Query unique values inside django forloop
Question:
I have a query where I should avoid double entry of the same question. In fact, I would like to get only unique values but, I am using the distinct()
django function which isn’t working.
I have these models:
class QuestionTopic(models.Model):
name = models.CharField(max_length=255)
question_subject = models.ForeignKey(
QuestionSubject, on_delete=models.CASCADE)
exam_questions_num = models.IntegerField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class Meta:
ordering = ('created_at',)
class Question(models.Model):
id = models.CharField(max_length=7,
unique=True,
primary_key=True,
editable=False)
question_subject = models.ForeignKey(
QuestionSubject, on_delete=models.CASCADE)
text = tinymce_models.HTMLField()
mark = models.IntegerField(default=1)
is_published = models.BooleanField(default=True)
question_bank_id = models.CharField(max_length=255, blank=True, null=True)
question_topic = models.ForeignKey(
QuestionTopic, on_delete=models.CASCADE, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
and the query is:
subject = QuestionSubject.objects.get(id=request.POST.get('subject'))
question_topics = QuestionTopic.objects.filter(
question_subject=subject)
questions_list = []
for topic in question_topics:
for q in range(topic.exam_questions_num):
question = Question.objects.filter(
question_subject=subject, question_topic=topic).values_list(
'id', flat=True).order_by('?').distinct().first()
questions_list.append(question)
What I would like to achieve is to have all different questions for each different topic inside questions_list
. Which I am not achieving at the moment being the distinct
used inside a loop.
Answers:
Just make questions_list a set:
questions_list = set()
Sets don’t allow a value more than once.
You can work with a single query with:
subject = QuestionSubject.objects.get(id=request.POST.get('subject'))
question_topics = QuestionTopic.objects.filter(question_subject=subject)
questions_list = [
question
for topic in question_topics
for question in Question.objects.filter(
question_subject=subject, question_topic=topic
).order_by('?')[: topic.exam_questions_num]
]
This will make O(n) queries with n the number of topics.
If the number of questions is not that large, you can do this with two queries with:
from random import sample
subject = QuestionSubject.objects.get(id=request.POST.get('subject'))
question_topics = QuestionTopic.objects.filter(
question_subject=subject
).prefetch_related('question_set')
questions_list = [
question
for topic in question_topics
for question in sample(topic.question_set.all(), topic.exam_question_num)
]
I have a query where I should avoid double entry of the same question. In fact, I would like to get only unique values but, I am using the distinct()
django function which isn’t working.
I have these models:
class QuestionTopic(models.Model):
name = models.CharField(max_length=255)
question_subject = models.ForeignKey(
QuestionSubject, on_delete=models.CASCADE)
exam_questions_num = models.IntegerField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class Meta:
ordering = ('created_at',)
class Question(models.Model):
id = models.CharField(max_length=7,
unique=True,
primary_key=True,
editable=False)
question_subject = models.ForeignKey(
QuestionSubject, on_delete=models.CASCADE)
text = tinymce_models.HTMLField()
mark = models.IntegerField(default=1)
is_published = models.BooleanField(default=True)
question_bank_id = models.CharField(max_length=255, blank=True, null=True)
question_topic = models.ForeignKey(
QuestionTopic, on_delete=models.CASCADE, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
and the query is:
subject = QuestionSubject.objects.get(id=request.POST.get('subject'))
question_topics = QuestionTopic.objects.filter(
question_subject=subject)
questions_list = []
for topic in question_topics:
for q in range(topic.exam_questions_num):
question = Question.objects.filter(
question_subject=subject, question_topic=topic).values_list(
'id', flat=True).order_by('?').distinct().first()
questions_list.append(question)
What I would like to achieve is to have all different questions for each different topic inside questions_list
. Which I am not achieving at the moment being the distinct
used inside a loop.
Just make questions_list a set:
questions_list = set()
Sets don’t allow a value more than once.
You can work with a single query with:
subject = QuestionSubject.objects.get(id=request.POST.get('subject'))
question_topics = QuestionTopic.objects.filter(question_subject=subject)
questions_list = [
question
for topic in question_topics
for question in Question.objects.filter(
question_subject=subject, question_topic=topic
).order_by('?')[: topic.exam_questions_num]
]
This will make O(n) queries with n the number of topics.
If the number of questions is not that large, you can do this with two queries with:
from random import sample
subject = QuestionSubject.objects.get(id=request.POST.get('subject'))
question_topics = QuestionTopic.objects.filter(
question_subject=subject
).prefetch_related('question_set')
questions_list = [
question
for topic in question_topics
for question in sample(topic.question_set.all(), topic.exam_question_num)
]