diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 7ada8e85d0..291ce8298f 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -143,9 +143,12 @@ class SQLCompiler(object): # then also add having expressions to group by. pk = None for expr in expressions: - if (expr.output_field.primary_key and - getattr(expr.output_field, 'model') == self.query.model): + # Is this a reference to query's base table primary key? If the + # expression isn't a Col-like, then skip the expression. + if (getattr(expr, 'target', None) == self.query.model._meta.pk and + getattr(expr, 'alias', None) == self.query.tables[0]): pk = expr + break if pk: # MySQLism: Columns in HAVING clause must be added to the GROUP BY. expressions = [pk] + [expr for expr in expressions if expr in having] diff --git a/docs/releases/1.8.2.txt b/docs/releases/1.8.2.txt index f430eb6cb0..bf54aeefd4 100644 --- a/docs/releases/1.8.2.txt +++ b/docs/releases/1.8.2.txt @@ -17,3 +17,6 @@ Bugfixes * Corrected join promotion for ``Case`` expressions. For example, annotating a query with a ``Case`` expression could unexpectedly filter out results (:ticket:`24766`). + +* Fixed incorrect GROUP BY clause generation on MySQL when the query's model + has a self-referential foreign key (:ticket:`24748`). diff --git a/tests/aggregation_regress/models.py b/tests/aggregation_regress/models.py index 2cad0a5486..4d675952c2 100644 --- a/tests/aggregation_regress/models.py +++ b/tests/aggregation_regress/models.py @@ -104,3 +104,8 @@ class Bravo(models.Model): class Charlie(models.Model): alfa = models.ForeignKey(Alfa, null=True) bravo = models.ForeignKey(Bravo, null=True) + + +class SelfRefFK(models.Model): + name = models.CharField(max_length=50) + parent = models.ForeignKey('self', null=True, blank=True, related_name='children') diff --git a/tests/aggregation_regress/tests.py b/tests/aggregation_regress/tests.py index 596f69c2dc..cb469363a0 100644 --- a/tests/aggregation_regress/tests.py +++ b/tests/aggregation_regress/tests.py @@ -17,7 +17,7 @@ from django.utils import six from .models import ( Alfa, Author, Book, Bravo, Charlie, Clues, Entries, HardbackBook, ItemTag, - Publisher, Store, WithManualPK, + Publisher, SelfRefFK, Store, WithManualPK, ) @@ -1277,3 +1277,15 @@ class JoinPromotionTests(TestCase): def test_non_nullable_fk_not_promoted(self): qs = Book.objects.annotate(Count('contact__name')) self.assertIn(' INNER JOIN ', str(qs.query)) + + +class SelfReferentialFKTests(TestCase): + def test_ticket_24748(self): + t1 = SelfRefFK.objects.create(name='t1') + SelfRefFK.objects.create(name='t2', parent=t1) + SelfRefFK.objects.create(name='t3', parent=t1) + self.assertQuerysetEqual( + SelfRefFK.objects.annotate(num_children=Count('children')).order_by('name'), + [('t1', 2), ('t2', 0), ('t3', 0)], + lambda x: (x.name, x.num_children) + )