mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +00:00 
			
		
		
		
	[1.8.x] Fixed #24748 -- Fixed incorrect GROUP BY on MySQL in some queries
When the query's model had a self-referential foreign key, the
compiler.get_group_by() code incorrectly used the self-referential
foreign key's column (for example parent_id) as GROUP BY clause
when it should have used the model's primary key column (id).
Backport of adc57632bc from master
			
			
This commit is contained in:
		
				
					committed by
					
						 Tim Graham
						Tim Graham
					
				
			
			
				
	
			
			
			
						parent
						
							056a91dbfa
						
					
				
				
					commit
					d5ce2dd7bc
				
			| @@ -145,9 +145,12 @@ class SQLCompiler(object): | |||||||
|             # then also add having expressions to group by. |             # then also add having expressions to group by. | ||||||
|             pk = None |             pk = None | ||||||
|             for expr in expressions: |             for expr in expressions: | ||||||
|                 if (expr.output_field.primary_key and |                 # Is this a reference to query's base table primary key? If the | ||||||
|                         getattr(expr.output_field, 'model') == self.query.model): |                 # 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 |                     pk = expr | ||||||
|  |                     break | ||||||
|             if pk: |             if pk: | ||||||
|                 expressions = [pk] + [expr for expr in expressions if expr in having] |                 expressions = [pk] + [expr for expr in expressions if expr in having] | ||||||
|         return expressions |         return expressions | ||||||
|   | |||||||
| @@ -17,3 +17,6 @@ Bugfixes | |||||||
| * Corrected join promotion for ``Case`` expressions. For example, annotating a | * Corrected join promotion for ``Case`` expressions. For example, annotating a | ||||||
|   query with a  ``Case`` expression could unexpectedly filter out results |   query with a  ``Case`` expression could unexpectedly filter out results | ||||||
|   (:ticket:`24766`). |   (:ticket:`24766`). | ||||||
|  |  | ||||||
|  | * Fixed incorrect GROUP BY clause generation on MySQL when the query's model | ||||||
|  |   has a self-referential foreign key (:ticket:`24748`). | ||||||
|   | |||||||
| @@ -104,3 +104,8 @@ class Bravo(models.Model): | |||||||
| class Charlie(models.Model): | class Charlie(models.Model): | ||||||
|     alfa = models.ForeignKey(Alfa, null=True) |     alfa = models.ForeignKey(Alfa, null=True) | ||||||
|     bravo = models.ForeignKey(Bravo, 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') | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ from django.utils import six | |||||||
|  |  | ||||||
| from .models import ( | from .models import ( | ||||||
|     Alfa, Author, Book, Bravo, Charlie, Clues, Entries, HardbackBook, ItemTag, |     Alfa, Author, Book, Bravo, Charlie, Clues, Entries, HardbackBook, ItemTag, | ||||||
|     Publisher, WithManualPK, |     Publisher, SelfRefFK, WithManualPK, | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -1174,28 +1174,13 @@ class JoinPromotionTests(TestCase): | |||||||
|         self.assertIn(' INNER JOIN ', str(qs.query)) |         self.assertIn(' INNER JOIN ', str(qs.query)) | ||||||
|  |  | ||||||
|  |  | ||||||
| class AggregationOnRelationTest(TestCase): | class SelfReferentialFKTests(TestCase): | ||||||
|     def setUp(self): |     def test_ticket_24748(self): | ||||||
|         self.a = Author.objects.create(name='Anssi', age=33) |         t1 = SelfRefFK.objects.create(name='t1') | ||||||
|         self.p = Publisher.objects.create(name='Manning', num_awards=3) |         SelfRefFK.objects.create(name='t2', parent=t1) | ||||||
|         Book.objects.create(isbn='asdf', name='Foo', pages=10, rating=0.1, price="0.0", |         SelfRefFK.objects.create(name='t3', parent=t1) | ||||||
|                             contact=self.a, publisher=self.p, pubdate=datetime.date.today()) |         self.assertQuerysetEqual( | ||||||
|  |             SelfRefFK.objects.annotate(num_children=Count('children')).order_by('name'), | ||||||
|     def test_annotate_on_relation(self): |             [('t1', 2), ('t2', 0), ('t3', 0)], | ||||||
|         qs = Book.objects.annotate(avg_price=Avg('price'), publisher_name=F('publisher__name')) |             lambda x: (x.name, x.num_children) | ||||||
|         self.assertEqual(qs[0].avg_price, 0.0) |  | ||||||
|         self.assertEqual(qs[0].publisher_name, "Manning") |  | ||||||
|  |  | ||||||
|     def test_aggregate_on_relation(self): |  | ||||||
|         # A query with an existing annotation aggregation on a relation should |  | ||||||
|         # succeed. |  | ||||||
|         qs = Book.objects.annotate(avg_price=Avg('price')).aggregate( |  | ||||||
|             publisher_awards=Sum('publisher__num_awards') |  | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(qs['publisher_awards'], 3) |  | ||||||
|         Book.objects.create(isbn='asdf', name='Foo', pages=10, rating=0.1, price="0.0", |  | ||||||
|                             contact=self.a, publisher=self.p, pubdate=datetime.date.today()) |  | ||||||
|         qs = Book.objects.annotate(avg_price=Avg('price')).aggregate( |  | ||||||
|             publisher_awards=Sum('publisher__num_awards') |  | ||||||
|         ) |  | ||||||
|         self.assertEqual(qs['publisher_awards'], 6) |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user