It's a code that reproduces the problem I experienced.
models.py
from django.db import models
class User(models.Model):
pass
class Group(models.Model):
users = models.ManyToManyField(
User,
related_name='groups',
)
class Notice(models.Model):
groups = models.ManyToManyField(
Group,
related_name='notices',
)
tests.py
from django.test import TestCase
from tests.models import User, Group, Notice
from django.db.models.aggregates import Count
def print_user_count(group):
count = group.users.count()
annotate_count = (
Group.objects
.annotate(
user_count=Count('users'),
notice_count=Count('notices')
)
.get(id=group.id)
.user_count
)
print('count: %d, annotate_count: %d' % (count, annotate_count))
class ModelTestCase(TestCase):
def test_(self):
user1 = User.objects.create()
group1 = Group.objects.create()
group1.users.set([user1])
print_user_count(group1) # count: 1, annotate_count: 1
for _ in range(5):
notice = Notice.objects.create()
notice.groups.set([group1])
print_user_count(group1) # count: 1, annotate_count: 5
I didn't add users to the group.
But the value obtained using annotate has increased from 1 to 5.
Is this a bug? Or did I use something wrong?
CodePudding user response:
This is not a problem, actually this is explained nicely in the documentation:
Combining multiple aggregations with annotate() will yield the wrong results because joins are used instead of subqueries:
Possible solution:
- Use
Count('users', distinct=True)
. - Use subquery.