Skip to content
Advertisement

Query unique values inside django forloop

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.

Advertisement

Answer

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)
]
Advertisement