I'm getting incorrect values ​when counting annotations

21 views Asked by At

When I filter with one or two tags, the number of likes is displayed correctly. However, if I filter by three tags, the number of likes is multiplied by the number of tags associated with the question. Otherwise, the function works correctly. The like values themselves do not change in the database

def tagAndFilter(request):
    tags_string = request.GET.get('tags', None)
    
    if tags_string:
        tags_list = [tag.strip() for tag in tags_string.split(',') if tag.strip()]

        tags = QuestionTag.objects.filter(name__in=tags_list)
        
        questions = Question.objects.filter(tags__in=tags).distinct()
        
        questions = questions.annotate(likescount=Count('likes'))

    context = {
        'questions': questions
    }
    return render(request, 'question/category.html', context)

models.py

class Like(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='likes')
    created_at = models.DateTimeField(auto_now_add=True)

as I understand it, the “multiplication” of results occurs in the line questions = questions.annotate(likescount=Count('likes'))

1

There are 1 answers

0
willeM_ Van Onsem On

This happens because JOINs act as "multipliers" of each other, as a result, if you JOIN on multiple models, you will count each like that many times as there are matching Tags.

You can fix this with:

def tagAndFilter(request):
    tags_string = request.GET.get('tags', None)
    if tags_string:
        tags_list = [tag.strip() for tag in tags_string.split(',') if tag.strip()]
        questions = (
            Question.objects.filter(tags__name__in=tags_list)
            .annotate(likescount=Count('likes', distinct=True))
            .distinct()
        )
    context = {'questions': questions}
    return render(request, 'question/category.html', context)

There is also no need to first fetch the QuestionTags. We can do the JOINs directly.