diff --git a/tests/regressiontests/aggregation_regress/models.py b/tests/regressiontests/aggregation_regress/models.py index dd4ff50aec0..b857ba62ac7 100644 --- a/tests/regressiontests/aggregation_regress/models.py +++ b/tests/regressiontests/aggregation_regress/models.py @@ -1,4 +1,6 @@ # coding: utf-8 +from django.contrib.contenttypes import generic +from django.contrib.contenttypes.models import ContentType from django.db import models from django.utils.encoding import python_2_unicode_compatible @@ -22,6 +24,13 @@ class Publisher(models.Model): return self.name +class TaggedItem(models.Model): + tag = models.CharField(max_length=100) + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey('content_type', 'object_id') + + @python_2_unicode_compatible class Book(models.Model): isbn = models.CharField(max_length=9) @@ -33,6 +42,7 @@ class Book(models.Model): contact = models.ForeignKey(Author, related_name='book_contact_set') publisher = models.ForeignKey(Publisher) pubdate = models.DateField() + tags = generic.GenericRelation(TaggedItem) class Meta: ordering = ('name',) @@ -63,6 +73,14 @@ class Clues(models.Model): Clue = models.CharField(max_length=150) +class WithManualPK(models.Model): + # The generic relations regression test needs two different model + # classes with the same PK value, and there are some (external) + # DB backends that don't work nicely when assigning integer to AutoField + # column (MSSQL at least). + id = models.IntegerField(primary_key=True) + + @python_2_unicode_compatible class HardbackBook(Book): weight = models.FloatField() diff --git a/tests/regressiontests/aggregation_regress/tests.py b/tests/regressiontests/aggregation_regress/tests.py index 076567538b8..94a02cf3b5c 100644 --- a/tests/regressiontests/aggregation_regress/tests.py +++ b/tests/regressiontests/aggregation_regress/tests.py @@ -6,11 +6,13 @@ from decimal import Decimal from operator import attrgetter from django.core.exceptions import FieldError +from django.contrib.contenttypes.models import ContentType from django.db.models import Count, Max, Avg, Sum, StdDev, Variance, F, Q from django.test import TestCase, Approximate, skipUnlessDBFeature from django.utils import six -from .models import Author, Book, Publisher, Clues, Entries, HardbackBook +from .models import (Author, Book, Publisher, Clues, Entries, HardbackBook, + TaggedItem, WithManualPK) class AggregationTests(TestCase): @@ -982,3 +984,39 @@ class AggregationTests(TestCase): def test_reverse_join_trimming(self): qs = Author.objects.annotate(Count('book_contact_set__contact')) self.assertIn(' JOIN ', str(qs.query)) + + def test_aggregation_with_generic_reverse_relation(self): + """ + Regression test for #10870: Aggregates with joins ignore extra + filters provided by setup_joins + + tests aggregations with generic reverse relations + """ + b = Book.objects.get(name='Practical Django Projects') + TaggedItem.objects.create(object_id=b.id, tag='intermediate', + content_type=ContentType.objects.get_for_model(b)) + TaggedItem.objects.create(object_id=b.id, tag='django', + content_type=ContentType.objects.get_for_model(b)) + # Assign a tag to model with same PK as the book above. If the JOIN + # used in aggregation doesn't have content type as part of the + # condition the annotation will also count the 'hi mom' tag for b. + wmpk = WithManualPK.objects.create(id=b.pk) + TaggedItem.objects.create(object_id=wmpk.id, tag='hi mom', + content_type=ContentType.objects.get_for_model(wmpk)) + b = Book.objects.get(name__startswith='Paradigms of Artificial Intelligence') + TaggedItem.objects.create(object_id=b.id, tag='intermediate', + content_type=ContentType.objects.get_for_model(b)) + + self.assertEqual(Book.objects.aggregate(Count('tags')), {'tags__count': 3}) + results = Book.objects.annotate(Count('tags')).order_by('-tags__count', 'name') + self.assertEqual( + [(b.name, b.tags__count) for b in results], + [ + ('Practical Django Projects', 2), + ('Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp', 1), + ('Artificial Intelligence: A Modern Approach', 0), + ('Python Web Development with Django', 0), + ('Sams Teach Yourself Django in 24 Hours', 0), + ('The Definitive Guide to Django: Web Development Done Right', 0) + ] + )