parts/django/tests/regressiontests/aggregation_regress/tests.py
changeset 307 c6bca38c1cbf
equal deleted inserted replaced
306:5ff1fc726848 307:c6bca38c1cbf
       
     1 import datetime
       
     2 import pickle
       
     3 from decimal import Decimal
       
     4 from operator import attrgetter
       
     5 
       
     6 from django.conf import settings
       
     7 from django.core.exceptions import FieldError
       
     8 from django.db import DEFAULT_DB_ALIAS
       
     9 from django.db.models import Count, Max, Avg, Sum, StdDev, Variance, F
       
    10 from django.test import TestCase, Approximate
       
    11 
       
    12 from models import Author, Book, Publisher, Clues, Entries, HardbackBook
       
    13 
       
    14 
       
    15 def run_stddev_tests():
       
    16     """Check to see if StdDev/Variance tests should be run.
       
    17 
       
    18     Stddev and Variance are not guaranteed to be available for SQLite, and
       
    19     are not available for PostgreSQL before 8.2.
       
    20     """
       
    21     if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == 'django.db.backends.sqlite3':
       
    22         return False
       
    23 
       
    24     class StdDevPop(object):
       
    25         sql_function = 'STDDEV_POP'
       
    26 
       
    27     try:
       
    28         connection.ops.check_aggregate_support(StdDevPop())
       
    29     except:
       
    30         return False
       
    31     return True
       
    32 
       
    33 
       
    34 class AggregationTests(TestCase):
       
    35     def assertObjectAttrs(self, obj, **kwargs):
       
    36         for attr, value in kwargs.iteritems():
       
    37             self.assertEqual(getattr(obj, attr), value)
       
    38 
       
    39     def test_aggregates_in_where_clause(self):
       
    40         """
       
    41         Regression test for #12822: DatabaseError: aggregates not allowed in
       
    42         WHERE clause
       
    43 
       
    44         Tests that the subselect works and returns results equivalent to a
       
    45         query with the IDs listed.
       
    46 
       
    47         Before the corresponding fix for this bug, this test passed in 1.1 and
       
    48         failed in 1.2-beta (trunk).
       
    49         """
       
    50         qs = Book.objects.values('contact').annotate(Max('id'))
       
    51         qs = qs.order_by('contact').values_list('id__max', flat=True)
       
    52         # don't do anything with the queryset (qs) before including it as a
       
    53         # subquery
       
    54         books = Book.objects.order_by('id')
       
    55         qs1 = books.filter(id__in=qs)
       
    56         qs2 = books.filter(id__in=list(qs))
       
    57         self.assertEqual(list(qs1), list(qs2))
       
    58 
       
    59     def test_aggregates_in_where_clause_pre_eval(self):
       
    60         """
       
    61         Regression test for #12822: DatabaseError: aggregates not allowed in
       
    62         WHERE clause
       
    63 
       
    64         Same as the above test, but evaluates the queryset for the subquery
       
    65         before it's used as a subquery.
       
    66 
       
    67         Before the corresponding fix for this bug, this test failed in both
       
    68         1.1 and 1.2-beta (trunk).
       
    69         """
       
    70         qs = Book.objects.values('contact').annotate(Max('id'))
       
    71         qs = qs.order_by('contact').values_list('id__max', flat=True)
       
    72         # force the queryset (qs) for the subquery to be evaluated in its
       
    73         # current state
       
    74         list(qs)
       
    75         books = Book.objects.order_by('id')
       
    76         qs1 = books.filter(id__in=qs)
       
    77         qs2 = books.filter(id__in=list(qs))
       
    78         self.assertEqual(list(qs1), list(qs2))
       
    79 
       
    80     if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.oracle':
       
    81         def test_annotate_with_extra(self):
       
    82             """
       
    83             Regression test for #11916: Extra params + aggregation creates
       
    84             incorrect SQL.
       
    85             """
       
    86             #oracle doesn't support subqueries in group by clause
       
    87             shortest_book_sql = """
       
    88             SELECT name
       
    89             FROM aggregation_regress_book b
       
    90             WHERE b.publisher_id = aggregation_regress_publisher.id
       
    91             ORDER BY b.pages
       
    92             LIMIT 1
       
    93             """
       
    94             # tests that this query does not raise a DatabaseError due to the full
       
    95             # subselect being (erroneously) added to the GROUP BY parameters
       
    96             qs = Publisher.objects.extra(select={
       
    97                 'name_of_shortest_book': shortest_book_sql,
       
    98             }).annotate(total_books=Count('book'))
       
    99             # force execution of the query
       
   100             list(qs)
       
   101 
       
   102     def test_aggregate(self):
       
   103         # Ordering requests are ignored
       
   104         self.assertEqual(
       
   105             Author.objects.order_by("name").aggregate(Avg("age")),
       
   106             {"age__avg": Approximate(37.444, places=1)}
       
   107         )
       
   108 
       
   109         # Implicit ordering is also ignored
       
   110         self.assertEqual(
       
   111             Book.objects.aggregate(Sum("pages")),
       
   112             {"pages__sum": 3703},
       
   113         )
       
   114 
       
   115         # Baseline results
       
   116         self.assertEqual(
       
   117             Book.objects.aggregate(Sum('pages'), Avg('pages')),
       
   118             {'pages__sum': 3703, 'pages__avg': Approximate(617.166, places=2)}
       
   119         )
       
   120 
       
   121         # Empty values query doesn't affect grouping or results
       
   122         self.assertEqual(
       
   123             Book.objects.values().aggregate(Sum('pages'), Avg('pages')),
       
   124             {'pages__sum': 3703, 'pages__avg': Approximate(617.166, places=2)}
       
   125         )
       
   126 
       
   127         # Aggregate overrides extra selected column
       
   128         self.assertEqual(
       
   129             Book.objects.extra(select={'price_per_page' : 'price / pages'}).aggregate(Sum('pages')),
       
   130             {'pages__sum': 3703}
       
   131         )
       
   132 
       
   133     def test_annotation(self):
       
   134         # Annotations get combined with extra select clauses
       
   135         obj = Book.objects.annotate(mean_auth_age=Avg("authors__age")).extra(select={"manufacture_cost": "price * .5"}).get(pk=2)
       
   136         self.assertObjectAttrs(obj,
       
   137             contact_id=3,
       
   138             id=2,
       
   139             isbn=u'067232959',
       
   140             mean_auth_age=45.0,
       
   141             name='Sams Teach Yourself Django in 24 Hours',
       
   142             pages=528,
       
   143             price=Decimal("23.09"),
       
   144             pubdate=datetime.date(2008, 3, 3),
       
   145             publisher_id=2,
       
   146             rating=3.0
       
   147         )
       
   148         # Different DB backends return different types for the extra select computation
       
   149         self.assertTrue(obj.manufacture_cost == 11.545 or obj.manufacture_cost == Decimal('11.545'))
       
   150 
       
   151         # Order of the annotate/extra in the query doesn't matter
       
   152         obj = Book.objects.extra(select={'manufacture_cost' : 'price * .5'}).annotate(mean_auth_age=Avg('authors__age')).get(pk=2)
       
   153         self.assertObjectAttrs(obj,
       
   154             contact_id=3,
       
   155             id=2,
       
   156             isbn=u'067232959',
       
   157             mean_auth_age=45.0,
       
   158             name=u'Sams Teach Yourself Django in 24 Hours',
       
   159             pages=528,
       
   160             price=Decimal("23.09"),
       
   161             pubdate=datetime.date(2008, 3, 3),
       
   162             publisher_id=2,
       
   163             rating=3.0
       
   164         )
       
   165         # Different DB backends return different types for the extra select computation
       
   166         self.assertTrue(obj.manufacture_cost == 11.545 or obj.manufacture_cost == Decimal('11.545'))
       
   167 
       
   168         # Values queries can be combined with annotate and extra
       
   169         obj = Book.objects.annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).values().get(pk=2)
       
   170         manufacture_cost = obj['manufacture_cost']
       
   171         self.assertTrue(manufacture_cost == 11.545 or manufacture_cost == Decimal('11.545'))
       
   172         del obj['manufacture_cost']
       
   173         self.assertEqual(obj, {
       
   174             "contact_id": 3,
       
   175             "id": 2,
       
   176             "isbn": u"067232959",
       
   177             "mean_auth_age": 45.0,
       
   178             "name": u"Sams Teach Yourself Django in 24 Hours",
       
   179             "pages": 528,
       
   180             "price": Decimal("23.09"),
       
   181             "pubdate": datetime.date(2008, 3, 3),
       
   182             "publisher_id": 2,
       
   183             "rating": 3.0,
       
   184         })
       
   185 
       
   186         # The order of the (empty) values, annotate and extra clauses doesn't
       
   187         # matter
       
   188         obj = Book.objects.values().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).get(pk=2)
       
   189         manufacture_cost = obj['manufacture_cost']
       
   190         self.assertTrue(manufacture_cost == 11.545 or manufacture_cost == Decimal('11.545'))
       
   191         del obj['manufacture_cost']
       
   192         self.assertEqual(obj, {
       
   193             'contact_id': 3,
       
   194             'id': 2,
       
   195             'isbn': u'067232959',
       
   196             'mean_auth_age': 45.0,
       
   197             'name': u'Sams Teach Yourself Django in 24 Hours',
       
   198             'pages': 528,
       
   199             'price': Decimal("23.09"),
       
   200             'pubdate': datetime.date(2008, 3, 3),
       
   201             'publisher_id': 2,
       
   202             'rating': 3.0
       
   203         })
       
   204 
       
   205         # If the annotation precedes the values clause, it won't be included
       
   206         # unless it is explicitly named
       
   207         obj = Book.objects.annotate(mean_auth_age=Avg('authors__age')).extra(select={'price_per_page' : 'price / pages'}).values('name').get(pk=1)
       
   208         self.assertEqual(obj, {
       
   209             "name": u'The Definitive Guide to Django: Web Development Done Right',
       
   210         })
       
   211 
       
   212         obj = Book.objects.annotate(mean_auth_age=Avg('authors__age')).extra(select={'price_per_page' : 'price / pages'}).values('name','mean_auth_age').get(pk=1)
       
   213         self.assertEqual(obj, {
       
   214             'mean_auth_age': 34.5,
       
   215             'name': u'The Definitive Guide to Django: Web Development Done Right',
       
   216         })
       
   217 
       
   218         # If an annotation isn't included in the values, it can still be used
       
   219         # in a filter
       
   220         qs = Book.objects.annotate(n_authors=Count('authors')).values('name').filter(n_authors__gt=2)
       
   221         self.assertQuerysetEqual(
       
   222             qs, [
       
   223                 {"name": u'Python Web Development with Django'}
       
   224             ],
       
   225             lambda b: b,
       
   226         )
       
   227 
       
   228         # The annotations are added to values output if values() precedes
       
   229         # annotate()
       
   230         obj = Book.objects.values('name').annotate(mean_auth_age=Avg('authors__age')).extra(select={'price_per_page' : 'price / pages'}).get(pk=1)
       
   231         self.assertEqual(obj, {
       
   232             'mean_auth_age': 34.5,
       
   233             'name': u'The Definitive Guide to Django: Web Development Done Right',
       
   234         })
       
   235 
       
   236         # Check that all of the objects are getting counted (allow_nulls) and
       
   237         # that values respects the amount of objects
       
   238         self.assertEqual(
       
   239             len(Author.objects.annotate(Avg('friends__age')).values()),
       
   240             9
       
   241         )
       
   242 
       
   243         # Check that consecutive calls to annotate accumulate in the query
       
   244         qs = Book.objects.values('price').annotate(oldest=Max('authors__age')).order_by('oldest', 'price').annotate(Max('publisher__num_awards'))
       
   245         self.assertQuerysetEqual(
       
   246             qs, [
       
   247                 {'price': Decimal("30"), 'oldest': 35, 'publisher__num_awards__max': 3},
       
   248                 {'price': Decimal("29.69"), 'oldest': 37, 'publisher__num_awards__max': 7},
       
   249                 {'price': Decimal("23.09"), 'oldest': 45, 'publisher__num_awards__max': 1},
       
   250                 {'price': Decimal("75"), 'oldest': 57, 'publisher__num_awards__max': 9},
       
   251                 {'price': Decimal("82.8"), 'oldest': 57, 'publisher__num_awards__max': 7}
       
   252             ],
       
   253             lambda b: b,
       
   254         )
       
   255 
       
   256     def test_aggrate_annotation(self):
       
   257         # Aggregates can be composed over annotations.
       
   258         # The return type is derived from the composed aggregate
       
   259         vals = Book.objects.all().annotate(num_authors=Count('authors__id')).aggregate(Max('pages'), Max('price'), Sum('num_authors'), Avg('num_authors'))
       
   260         self.assertEqual(vals, {
       
   261             'num_authors__sum': 10,
       
   262             'num_authors__avg': Approximate(1.666, places=2),
       
   263             'pages__max': 1132,
       
   264             'price__max': Decimal("82.80")
       
   265         })
       
   266 
       
   267     def test_field_error(self):
       
   268         # Bad field requests in aggregates are caught and reported
       
   269         self.assertRaises(
       
   270             FieldError,
       
   271             lambda: Book.objects.all().aggregate(num_authors=Count('foo'))
       
   272         )
       
   273 
       
   274         self.assertRaises(
       
   275             FieldError,
       
   276             lambda: Book.objects.all().annotate(num_authors=Count('foo'))
       
   277         )
       
   278 
       
   279         self.assertRaises(
       
   280             FieldError,
       
   281             lambda: Book.objects.all().annotate(num_authors=Count('authors__id')).aggregate(Max('foo'))
       
   282         )
       
   283 
       
   284     def test_more(self):
       
   285         # Old-style count aggregations can be mixed with new-style
       
   286         self.assertEqual(
       
   287             Book.objects.annotate(num_authors=Count('authors')).count(),
       
   288             6
       
   289         )
       
   290 
       
   291         # Non-ordinal, non-computed Aggregates over annotations correctly
       
   292         # inherit the annotation's internal type if the annotation is ordinal
       
   293         # or computed
       
   294         vals = Book.objects.annotate(num_authors=Count('authors')).aggregate(Max('num_authors'))
       
   295         self.assertEqual(
       
   296             vals,
       
   297             {'num_authors__max': 3}
       
   298         )
       
   299 
       
   300         vals = Publisher.objects.annotate(avg_price=Avg('book__price')).aggregate(Max('avg_price'))
       
   301         self.assertEqual(
       
   302             vals,
       
   303             {'avg_price__max': 75.0}
       
   304         )
       
   305 
       
   306         # Aliases are quoted to protected aliases that might be reserved names
       
   307         vals = Book.objects.aggregate(number=Max('pages'), select=Max('pages'))
       
   308         self.assertEqual(
       
   309             vals,
       
   310             {'number': 1132, 'select': 1132}
       
   311         )
       
   312 
       
   313         # Regression for #10064: select_related() plays nice with aggregates
       
   314         obj = Book.objects.select_related('publisher').annotate(num_authors=Count('authors')).values()[0]
       
   315         self.assertEqual(obj, {
       
   316             'contact_id': 8,
       
   317             'id': 5,
       
   318             'isbn': u'013790395',
       
   319             'name': u'Artificial Intelligence: A Modern Approach',
       
   320             'num_authors': 2,
       
   321             'pages': 1132,
       
   322             'price': Decimal("82.8"),
       
   323             'pubdate': datetime.date(1995, 1, 15),
       
   324             'publisher_id': 3,
       
   325             'rating': 4.0,
       
   326         })
       
   327 
       
   328         # Regression for #10010: exclude on an aggregate field is correctly
       
   329         # negated
       
   330         self.assertEqual(
       
   331             len(Book.objects.annotate(num_authors=Count('authors'))),
       
   332             6
       
   333         )
       
   334         self.assertEqual(
       
   335             len(Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=2)),
       
   336             1
       
   337         )
       
   338         self.assertEqual(
       
   339             len(Book.objects.annotate(num_authors=Count('authors')).exclude(num_authors__gt=2)),
       
   340             5
       
   341         )
       
   342 
       
   343         self.assertEqual(
       
   344             len(Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__lt=3).exclude(num_authors__lt=2)),
       
   345             2
       
   346         )
       
   347         self.assertEqual(
       
   348             len(Book.objects.annotate(num_authors=Count('authors')).exclude(num_authors__lt=2).filter(num_authors__lt=3)),
       
   349             2
       
   350         )
       
   351 
       
   352     def test_aggregate_fexpr(self):
       
   353         # Aggregates can be used with F() expressions
       
   354         # ... where the F() is pushed into the HAVING clause
       
   355         qs = Publisher.objects.annotate(num_books=Count('book')).filter(num_books__lt=F('num_awards')/2).order_by('name').values('name','num_books','num_awards')
       
   356         self.assertQuerysetEqual(
       
   357             qs, [
       
   358                 {'num_books': 1, 'name': u'Morgan Kaufmann', 'num_awards': 9},
       
   359                 {'num_books': 2, 'name': u'Prentice Hall', 'num_awards': 7}
       
   360             ],
       
   361             lambda p: p,
       
   362         )
       
   363 
       
   364         qs = Publisher.objects.annotate(num_books=Count('book')).exclude(num_books__lt=F('num_awards')/2).order_by('name').values('name','num_books','num_awards')
       
   365         self.assertQuerysetEqual(
       
   366             qs, [
       
   367                 {'num_books': 2, 'name': u'Apress', 'num_awards': 3},
       
   368                 {'num_books': 0, 'name': u"Jonno's House of Books", 'num_awards': 0},
       
   369                 {'num_books': 1, 'name': u'Sams', 'num_awards': 1}
       
   370             ],
       
   371             lambda p: p,
       
   372         )
       
   373 
       
   374         # ... and where the F() references an aggregate
       
   375         qs = Publisher.objects.annotate(num_books=Count('book')).filter(num_awards__gt=2*F('num_books')).order_by('name').values('name','num_books','num_awards')
       
   376         self.assertQuerysetEqual(
       
   377             qs, [
       
   378                 {'num_books': 1, 'name': u'Morgan Kaufmann', 'num_awards': 9},
       
   379                 {'num_books': 2, 'name': u'Prentice Hall', 'num_awards': 7}
       
   380             ],
       
   381             lambda p: p,
       
   382         )
       
   383 
       
   384         qs = Publisher.objects.annotate(num_books=Count('book')).exclude(num_books__lt=F('num_awards')/2).order_by('name').values('name','num_books','num_awards')
       
   385         self.assertQuerysetEqual(
       
   386             qs, [
       
   387                 {'num_books': 2, 'name': u'Apress', 'num_awards': 3},
       
   388                 {'num_books': 0, 'name': u"Jonno's House of Books", 'num_awards': 0},
       
   389                 {'num_books': 1, 'name': u'Sams', 'num_awards': 1}
       
   390             ],
       
   391             lambda p: p,
       
   392         )
       
   393 
       
   394     def test_db_col_table(self):
       
   395         # Tests on fields with non-default table and column names.
       
   396         qs = Clues.objects.values('EntryID__Entry').annotate(Appearances=Count('EntryID'), Distinct_Clues=Count('Clue', distinct=True))
       
   397         self.assertQuerysetEqual(qs, [])
       
   398 
       
   399         qs = Entries.objects.annotate(clue_count=Count('clues__ID'))
       
   400         self.assertQuerysetEqual(qs, [])
       
   401 
       
   402     def test_empty(self):
       
   403         # Regression for #10089: Check handling of empty result sets with
       
   404         # aggregates
       
   405         self.assertEqual(
       
   406             Book.objects.filter(id__in=[]).count(),
       
   407             0
       
   408         )
       
   409 
       
   410         vals = Book.objects.filter(id__in=[]).aggregate(num_authors=Count('authors'), avg_authors=Avg('authors'), max_authors=Max('authors'), max_price=Max('price'), max_rating=Max('rating'))
       
   411         self.assertEqual(
       
   412             vals,
       
   413             {'max_authors': None, 'max_rating': None, 'num_authors': 0, 'avg_authors': None, 'max_price': None}
       
   414         )
       
   415 
       
   416         qs = Publisher.objects.filter(pk=5).annotate(num_authors=Count('book__authors'), avg_authors=Avg('book__authors'), max_authors=Max('book__authors'), max_price=Max('book__price'), max_rating=Max('book__rating')).values()
       
   417         self.assertQuerysetEqual(
       
   418             qs, [
       
   419                 {'max_authors': None, 'name': u"Jonno's House of Books", 'num_awards': 0, 'max_price': None, 'num_authors': 0, 'max_rating': None, 'id': 5, 'avg_authors': None}
       
   420             ],
       
   421             lambda p: p
       
   422         )
       
   423 
       
   424     def test_more_more(self):
       
   425         # Regression for #10113 - Fields mentioned in order_by() must be
       
   426         # included in the GROUP BY. This only becomes a problem when the
       
   427         # order_by introduces a new join.
       
   428         self.assertQuerysetEqual(
       
   429             Book.objects.annotate(num_authors=Count('authors')).order_by('publisher__name', 'name'), [
       
   430                 "Practical Django Projects",
       
   431                 "The Definitive Guide to Django: Web Development Done Right",
       
   432                 "Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp",
       
   433                 "Artificial Intelligence: A Modern Approach",
       
   434                 "Python Web Development with Django",
       
   435                 "Sams Teach Yourself Django in 24 Hours",
       
   436             ],
       
   437             lambda b: b.name
       
   438         )
       
   439 
       
   440         # Regression for #10127 - Empty select_related() works with annotate
       
   441         qs = Book.objects.filter(rating__lt=4.5).select_related().annotate(Avg('authors__age'))
       
   442         self.assertQuerysetEqual(
       
   443             qs, [
       
   444                 (u'Artificial Intelligence: A Modern Approach', 51.5, u'Prentice Hall', u'Peter Norvig'),
       
   445                 (u'Practical Django Projects', 29.0, u'Apress', u'James Bennett'),
       
   446                 (u'Python Web Development with Django', Approximate(30.333, places=2), u'Prentice Hall', u'Jeffrey Forcier'),
       
   447                 (u'Sams Teach Yourself Django in 24 Hours', 45.0, u'Sams', u'Brad Dayley')
       
   448             ],
       
   449             lambda b: (b.name, b.authors__age__avg, b.publisher.name, b.contact.name)
       
   450         )
       
   451 
       
   452         # Regression for #10132 - If the values() clause only mentioned extra
       
   453         # (select=) columns, those columns are used for grouping
       
   454         qs = Book.objects.extra(select={'pub':'publisher_id'}).values('pub').annotate(Count('id')).order_by('pub')
       
   455         self.assertQuerysetEqual(
       
   456             qs, [
       
   457                 {'pub': 1, 'id__count': 2},
       
   458                 {'pub': 2, 'id__count': 1},
       
   459                 {'pub': 3, 'id__count': 2},
       
   460                 {'pub': 4, 'id__count': 1}
       
   461             ],
       
   462             lambda b: b
       
   463         )
       
   464 
       
   465         qs = Book.objects.extra(select={'pub':'publisher_id', 'foo':'pages'}).values('pub').annotate(Count('id')).order_by('pub')
       
   466         self.assertQuerysetEqual(
       
   467             qs, [
       
   468                 {'pub': 1, 'id__count': 2},
       
   469                 {'pub': 2, 'id__count': 1},
       
   470                 {'pub': 3, 'id__count': 2},
       
   471                 {'pub': 4, 'id__count': 1}
       
   472             ],
       
   473             lambda b: b
       
   474         )
       
   475 
       
   476         # Regression for #10182 - Queries with aggregate calls are correctly
       
   477         # realiased when used in a subquery
       
   478         ids = Book.objects.filter(pages__gt=100).annotate(n_authors=Count('authors')).filter(n_authors__gt=2).order_by('n_authors')
       
   479         self.assertQuerysetEqual(
       
   480             Book.objects.filter(id__in=ids), [
       
   481                 "Python Web Development with Django",
       
   482             ],
       
   483             lambda b: b.name
       
   484         )
       
   485 
       
   486     def test_duplicate_alias(self):
       
   487         # Regression for #11256 - duplicating a default alias raises ValueError.
       
   488         self.assertRaises(ValueError, Book.objects.all().annotate, Avg('authors__age'), authors__age__avg=Avg('authors__age'))
       
   489 
       
   490     def test_field_name_conflict(self):
       
   491         # Regression for #11256 - providing an aggregate name that conflicts with a field name on the model raises ValueError
       
   492         self.assertRaises(ValueError, Author.objects.annotate, age=Avg('friends__age'))
       
   493 
       
   494     def test_m2m_name_conflict(self):
       
   495         # Regression for #11256 - providing an aggregate name that conflicts with an m2m name on the model raises ValueError
       
   496         self.assertRaises(ValueError, Author.objects.annotate, friends=Count('friends'))
       
   497 
       
   498     def test_reverse_relation_name_conflict(self):
       
   499         # Regression for #11256 - providing an aggregate name that conflicts with a reverse-related name on the model raises ValueError
       
   500         self.assertRaises(ValueError, Author.objects.annotate, book_contact_set=Avg('friends__age'))
       
   501 
       
   502     def test_pickle(self):
       
   503         # Regression for #10197 -- Queries with aggregates can be pickled.
       
   504         # First check that pickling is possible at all. No crash = success
       
   505         qs = Book.objects.annotate(num_authors=Count('authors'))
       
   506         pickle.dumps(qs)
       
   507 
       
   508         # Then check that the round trip works.
       
   509         query = qs.query.get_compiler(qs.db).as_sql()[0]
       
   510         qs2 = pickle.loads(pickle.dumps(qs))
       
   511         self.assertEqual(
       
   512             qs2.query.get_compiler(qs2.db).as_sql()[0],
       
   513             query,
       
   514         )
       
   515 
       
   516     def test_more_more_more(self):
       
   517         # Regression for #10199 - Aggregate calls clone the original query so
       
   518         # the original query can still be used
       
   519         books = Book.objects.all()
       
   520         books.aggregate(Avg("authors__age"))
       
   521         self.assertQuerysetEqual(
       
   522             books.all(), [
       
   523                 u'Artificial Intelligence: A Modern Approach',
       
   524                 u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp',
       
   525                 u'Practical Django Projects',
       
   526                 u'Python Web Development with Django',
       
   527                 u'Sams Teach Yourself Django in 24 Hours',
       
   528                 u'The Definitive Guide to Django: Web Development Done Right'
       
   529             ],
       
   530             lambda b: b.name
       
   531         )
       
   532 
       
   533         # Regression for #10248 - Annotations work with DateQuerySets
       
   534         qs = Book.objects.annotate(num_authors=Count('authors')).filter(num_authors=2).dates('pubdate', 'day')
       
   535         self.assertQuerysetEqual(
       
   536             qs, [
       
   537                 datetime.datetime(1995, 1, 15, 0, 0),
       
   538                 datetime.datetime(2007, 12, 6, 0, 0)
       
   539             ],
       
   540             lambda b: b
       
   541         )
       
   542 
       
   543         # Regression for #10290 - extra selects with parameters can be used for
       
   544         # grouping.
       
   545         qs = Book.objects.annotate(mean_auth_age=Avg('authors__age')).extra(select={'sheets' : '(pages + %s) / %s'}, select_params=[1, 2]).order_by('sheets').values('sheets')
       
   546         self.assertQuerysetEqual(
       
   547             qs, [
       
   548                 150,
       
   549                 175,
       
   550                 224,
       
   551                 264,
       
   552                 473,
       
   553                 566
       
   554             ],
       
   555             lambda b: int(b["sheets"])
       
   556         )
       
   557 
       
   558         # Regression for 10425 - annotations don't get in the way of a count()
       
   559         # clause
       
   560         self.assertEqual(
       
   561             Book.objects.values('publisher').annotate(Count('publisher')).count(),
       
   562             4
       
   563         )
       
   564         self.assertEqual(
       
   565             Book.objects.annotate(Count('publisher')).values('publisher').count(),
       
   566             6
       
   567         )
       
   568 
       
   569         publishers = Publisher.objects.filter(id__in=[1, 2])
       
   570         self.assertQuerysetEqual(
       
   571             publishers, [
       
   572                 "Apress",
       
   573                 "Sams"
       
   574             ],
       
   575             lambda p: p.name
       
   576         )
       
   577 
       
   578         publishers = publishers.annotate(n_books=Count("book"))
       
   579         self.assertEqual(
       
   580             publishers[0].n_books,
       
   581             2
       
   582         )
       
   583 
       
   584         self.assertQuerysetEqual(
       
   585             publishers, [
       
   586                 "Apress",
       
   587                 "Sams",
       
   588             ],
       
   589             lambda p: p.name
       
   590         )
       
   591 
       
   592         books = Book.objects.filter(publisher__in=publishers)
       
   593         self.assertQuerysetEqual(
       
   594             books, [
       
   595                 "Practical Django Projects",
       
   596                 "Sams Teach Yourself Django in 24 Hours",
       
   597                 "The Definitive Guide to Django: Web Development Done Right",
       
   598             ],
       
   599             lambda b: b.name
       
   600         )
       
   601         self.assertQuerysetEqual(
       
   602             publishers, [
       
   603                 "Apress",
       
   604                 "Sams",
       
   605             ],
       
   606             lambda p: p.name
       
   607         )
       
   608 
       
   609         # Regression for 10666 - inherited fields work with annotations and
       
   610         # aggregations
       
   611         self.assertEqual(
       
   612             HardbackBook.objects.aggregate(n_pages=Sum('book_ptr__pages')),
       
   613             {'n_pages': 2078}
       
   614         )
       
   615 
       
   616         self.assertEqual(
       
   617             HardbackBook.objects.aggregate(n_pages=Sum('pages')),
       
   618             {'n_pages': 2078},
       
   619         )
       
   620 
       
   621         qs = HardbackBook.objects.annotate(n_authors=Count('book_ptr__authors')).values('name', 'n_authors')
       
   622         self.assertQuerysetEqual(
       
   623             qs, [
       
   624                 {'n_authors': 2, 'name': u'Artificial Intelligence: A Modern Approach'},
       
   625                 {'n_authors': 1, 'name': u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp'}
       
   626             ],
       
   627             lambda h: h
       
   628         )
       
   629 
       
   630         qs = HardbackBook.objects.annotate(n_authors=Count('authors')).values('name', 'n_authors')
       
   631         self.assertQuerysetEqual(
       
   632             qs, [
       
   633                 {'n_authors': 2, 'name': u'Artificial Intelligence: A Modern Approach'},
       
   634                 {'n_authors': 1, 'name': u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp'}
       
   635             ],
       
   636             lambda h: h,
       
   637         )
       
   638 
       
   639         # Regression for #10766 - Shouldn't be able to reference an aggregate
       
   640         # fields in an an aggregate() call.
       
   641         self.assertRaises(
       
   642             FieldError,
       
   643             lambda: Book.objects.annotate(mean_age=Avg('authors__age')).annotate(Avg('mean_age'))
       
   644         )
       
   645 
       
   646     def test_empty_filter_count(self):
       
   647         self.assertEqual(
       
   648             Author.objects.filter(id__in=[]).annotate(Count("friends")).count(),
       
   649             0
       
   650         )
       
   651 
       
   652     def test_empty_filter_aggregate(self):
       
   653         self.assertEqual(
       
   654             Author.objects.filter(id__in=[]).annotate(Count("friends")).aggregate(Count("pk")),
       
   655             {"pk__count": None}
       
   656         )
       
   657 
       
   658     def test_annotate_and_join(self):
       
   659         self.assertEqual(
       
   660             Author.objects.annotate(c=Count("friends__name")).exclude(friends__name="Joe").count(),
       
   661             Author.objects.count()
       
   662         )
       
   663 
       
   664     def test_f_expression_annotation(self):
       
   665         # Books with less than 200 pages per author.
       
   666         qs = Book.objects.values("name").annotate(
       
   667             n_authors=Count("authors")
       
   668         ).filter(
       
   669             pages__lt=F("n_authors") * 200
       
   670         ).values_list("pk")
       
   671         self.assertQuerysetEqual(
       
   672             Book.objects.filter(pk__in=qs), [
       
   673                 "Python Web Development with Django"
       
   674             ],
       
   675             attrgetter("name")
       
   676         )
       
   677 
       
   678     def test_values_annotate_values(self):
       
   679         qs = Book.objects.values("name").annotate(
       
   680             n_authors=Count("authors")
       
   681         ).values_list("pk", flat=True)
       
   682         self.assertEqual(list(qs), list(Book.objects.values_list("pk", flat=True)))
       
   683 
       
   684     def test_having_group_by(self):
       
   685         # Test that when a field occurs on the LHS of a HAVING clause that it
       
   686         # appears correctly in the GROUP BY clause
       
   687         qs = Book.objects.values_list("name").annotate(
       
   688             n_authors=Count("authors")
       
   689         ).filter(
       
   690             pages__gt=F("n_authors")
       
   691         ).values_list("name", flat=True)
       
   692         # Results should be the same, all Books have more pages than authors
       
   693         self.assertEqual(
       
   694             list(qs), list(Book.objects.values_list("name", flat=True))
       
   695         )
       
   696 
       
   697     if run_stddev_tests():
       
   698         def test_stddev(self):
       
   699             self.assertEqual(
       
   700                 Book.objects.aggregate(StdDev('pages')),
       
   701                 {'pages__stddev': Approximate(311.46, 1)}
       
   702             )
       
   703 
       
   704             self.assertEqual(
       
   705                 Book.objects.aggregate(StdDev('rating')),
       
   706                 {'rating__stddev': Approximate(0.60, 1)}
       
   707             )
       
   708 
       
   709             self.assertEqual(
       
   710                 Book.objects.aggregate(StdDev('price')),
       
   711                 {'price__stddev': Approximate(24.16, 2)}
       
   712             )
       
   713 
       
   714             self.assertEqual(
       
   715                 Book.objects.aggregate(StdDev('pages', sample=True)),
       
   716                 {'pages__stddev': Approximate(341.19, 2)}
       
   717             )
       
   718 
       
   719             self.assertEqual(
       
   720                 Book.objects.aggregate(StdDev('rating', sample=True)),
       
   721                 {'rating__stddev': Approximate(0.66, 2)}
       
   722             )
       
   723 
       
   724             self.assertEqual(
       
   725                 Book.objects.aggregate(StdDev('price', sample=True)),
       
   726                 {'price__stddev': Approximate(26.46, 1)}
       
   727             )
       
   728 
       
   729             self.assertEqual(
       
   730                 Book.objects.aggregate(Variance('pages')),
       
   731                 {'pages__variance': Approximate(97010.80, 1)}
       
   732             )
       
   733 
       
   734             self.assertEqual(
       
   735                 Book.objects.aggregate(Variance('rating')),
       
   736                 {'rating__variance': Approximate(0.36, 1)}
       
   737             )
       
   738 
       
   739             self.assertEqual(
       
   740                 Book.objects.aggregate(Variance('price')),
       
   741                 {'price__variance': Approximate(583.77, 1)}
       
   742             )
       
   743 
       
   744             self.assertEqual(
       
   745                 Book.objects.aggregate(Variance('pages', sample=True)),
       
   746                 {'pages__variance': Approximate(116412.96, 1)}
       
   747             )
       
   748 
       
   749             self.assertEqual(
       
   750                 Book.objects.aggregate(Variance('rating', sample=True)),
       
   751                 {'rating__variance': Approximate(0.44, 2)}
       
   752             )
       
   753 
       
   754             self.assertEqual(
       
   755                 Book.objects.aggregate(Variance('price', sample=True)),
       
   756                 {'price__variance': Approximate(700.53, 2)}
       
   757             )