Ticket #13895: django-aggregation-regress-tests.diff

File django-aggregation-regress-tests.diff, 46.8 KB (added by Alex Gaynor, 14 years ago)
  • django/test/__init__.py

    diff --git a/django/test/__init__.py b/django/test/__init__.py
    index 957b293..c996ed4 100644
    a b Django Unit Test and Doctest framework.  
    44
    55from django.test.client import Client
    66from django.test.testcases import TestCase, TransactionTestCase
     7from django.test.utils import Approximate
  • django/test/utils.py

    diff --git a/django/test/utils.py b/django/test/utils.py
    index b6ab399..f38c60f 100644
    a b  
    1 import sys, time, os
     1import sys
     2import time
     3import os
    24from django.conf import settings
    35from django.core import mail
    46from django.core.mail.backends import locmem
    from django.test import signals  
    68from django.template import Template
    79from django.utils.translation import deactivate
    810
     11
     12class Approximate(object):
     13    def __init__(self, val, places=7):
     14        self.val = val
     15        self.places = places
     16
     17    def __repr__(self):
     18        return repr(self.val)
     19
     20    def __eq__(self, other):
     21        if self.val == other:
     22            return True
     23        return round(abs(self.val-other), self.places) == 0
     24
     25
    926class ContextList(list):
    1027    """A wrapper that provides direct key access to context items contained
    1128    in a list of context objects.
  • tests/modeltests/aggregation/tests.py

    diff --git a/tests/modeltests/aggregation/tests.py b/tests/modeltests/aggregation/tests.py
    index cb61143..c830368 100644
    a b import datetime  
    22from decimal import Decimal
    33
    44from django.db.models import Avg, Sum, Count, Max, Min
    5 from django.test import TestCase
     5from django.test import TestCase, Approximate
    66
    77from models import Author, Publisher, Book, Store
    88
    99
    10 class Approximate(object):
    11     def __init__(self, val, places=7):
    12         self.val = val
    13         self.places = places
    14 
    15     def __repr__(self):
    16         return repr(self.val)
    17 
    18     def __eq__(self, other):
    19         if self.val == other:
    20             return True
    21         return round(abs(self.val-other), self.places) == 0
    22 
    2310class BaseAggregateTestCase(TestCase):
    2411    fixtures = ["initial_data.json"]
    2512
  • tests/regressiontests/aggregation_regress/models.py

    diff --git a/tests/regressiontests/aggregation_regress/models.py b/tests/regressiontests/aggregation_regress/models.py
    index ba74357..783c219 100644
    a b import pickle  
    44from django.db import connection, models, DEFAULT_DB_ALIAS
    55from django.conf import settings
    66
     7
    78class Author(models.Model):
    89    name = models.CharField(max_length=100)
    910    age = models.IntegerField()
    class Author(models.Model):  
    1213    def __unicode__(self):
    1314        return self.name
    1415
     16
    1517class Publisher(models.Model):
    1618    name = models.CharField(max_length=255)
    1719    num_awards = models.IntegerField()
    class Publisher(models.Model):  
    1921    def __unicode__(self):
    2022        return self.name
    2123
     24
    2225class Book(models.Model):
    2326    isbn = models.CharField(max_length=9)
    2427    name = models.CharField(max_length=255)
    class Book(models.Model):  
    3639    def __unicode__(self):
    3740        return self.name
    3841
     42
    3943class Store(models.Model):
    4044    name = models.CharField(max_length=255)
    4145    books = models.ManyToManyField(Book)
    class Store(models.Model):  
    4549    def __unicode__(self):
    4650        return self.name
    4751
     52
    4853class Entries(models.Model):
    4954    EntryID = models.AutoField(primary_key=True, db_column='Entry ID')
    5055    Entry = models.CharField(unique=True, max_length=50)
    5156    Exclude = models.BooleanField()
    5257
     58
    5359class Clues(models.Model):
    5460    ID = models.AutoField(primary_key=True)
    5561    EntryID = models.ForeignKey(Entries, verbose_name='Entry', db_column = 'Entry ID')
    5662    Clue = models.CharField(max_length=150)
    5763
     64
    5865class HardbackBook(Book):
    5966    weight = models.FloatField()
    6067
    6168    def __unicode__(self):
    6269        return "%s (hardback): %s" % (self.name, self.weight)
    63 
    64 __test__ = {'API_TESTS': """
    65 >>> from django.core import management
    66 >>> from django.db.models import get_app, F
    67 
    68 # Reset the database representation of this app.
    69 # This will return the database to a clean initial state.
    70 >>> management.call_command('flush', verbosity=0, interactive=False)
    71 
    72 >>> from django.db.models import Avg, Sum, Count, Max, Min, StdDev, Variance
    73 
    74 # Ordering requests are ignored
    75 >>> Author.objects.all().order_by('name').aggregate(Avg('age'))
    76 {'age__avg': 37.4...}
    77 
    78 # Implicit ordering is also ignored
    79 >>> Book.objects.all().aggregate(Sum('pages'))
    80 {'pages__sum': 3703}
    81 
    82 # Baseline results
    83 >>> Book.objects.all().aggregate(Sum('pages'), Avg('pages'))
    84 {'pages__sum': 3703, 'pages__avg': 617.1...}
    85 
    86 # Empty values query doesn't affect grouping or results
    87 >>> Book.objects.all().values().aggregate(Sum('pages'), Avg('pages'))
    88 {'pages__sum': 3703, 'pages__avg': 617.1...}
    89 
    90 # Aggregate overrides extra selected column
    91 >>> Book.objects.all().extra(select={'price_per_page' : 'price / pages'}).aggregate(Sum('pages'))
    92 {'pages__sum': 3703}
    93 
    94 # Annotations get combined with extra select clauses
    95 >>> sorted((k,v) for k,v in Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).get(pk=2).__dict__.items() if k != '_state')
    96 [('contact_id', 3), ('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)]
    97 
    98 # Order of the annotate/extra in the query doesn't matter
    99 >>> sorted((k,v) for k,v in Book.objects.all().extra(select={'manufacture_cost' : 'price * .5'}).annotate(mean_auth_age=Avg('authors__age')).get(pk=2).__dict__.items()if k != '_state')
    100 [('contact_id', 3), ('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)]
    101 
    102 # Values queries can be combined with annotate and extra
    103 >>> sorted((k,v) for k,v in Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).values().get(pk=2).items()if k != '_state')
    104 [('contact_id', 3), ('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)]
    105 
    106 # The order of the (empty) values, annotate and extra clauses doesn't matter
    107 >>> sorted((k,v) for k,v in Book.objects.all().values().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).get(pk=2).items()if k != '_state')
    108 [('contact_id', 3), ('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)]
    109 
    110 # If the annotation precedes the values clause, it won't be included
    111 # unless it is explicitly named
    112 >>> sorted(Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'price_per_page' : 'price / pages'}).values('name').get(pk=1).items())
    113 [('name', u'The Definitive Guide to Django: Web Development Done Right')]
    114 
    115 >>> sorted(Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'price_per_page' : 'price / pages'}).values('name','mean_auth_age').get(pk=1).items())
    116 [('mean_auth_age', 34.5), ('name', u'The Definitive Guide to Django: Web Development Done Right')]
    117 
    118 # If an annotation isn't included in the values, it can still be used in a filter
    119 >>> Book.objects.annotate(n_authors=Count('authors')).values('name').filter(n_authors__gt=2)
    120 [{'name': u'Python Web Development with Django'}]
    121 
    122 # The annotations are added to values output if values() precedes annotate()
    123 >>> sorted(Book.objects.all().values('name').annotate(mean_auth_age=Avg('authors__age')).extra(select={'price_per_page' : 'price / pages'}).get(pk=1).items())
    124 [('mean_auth_age', 34.5), ('name', u'The Definitive Guide to Django: Web Development Done Right')]
    125 
    126 # Check that all of the objects are getting counted (allow_nulls) and that values respects the amount of objects
    127 >>> len(Author.objects.all().annotate(Avg('friends__age')).values())
    128 9
    129 
    130 # Check that consecutive calls to annotate accumulate in the query
    131 >>> Book.objects.values('price').annotate(oldest=Max('authors__age')).order_by('oldest', 'price').annotate(Max('publisher__num_awards'))
    132 [{'price': Decimal("30..."), 'oldest': 35, 'publisher__num_awards__max': 3}, {'price': Decimal("29.69"), 'oldest': 37, 'publisher__num_awards__max': 7}, {'price': Decimal("23.09"), 'oldest': 45, 'publisher__num_awards__max': 1}, {'price': Decimal("75..."), 'oldest': 57, 'publisher__num_awards__max': 9}, {'price': Decimal("82.8..."), 'oldest': 57, 'publisher__num_awards__max': 7}]
    133 
    134 # Aggregates can be composed over annotations.
    135 # The return type is derived from the composed aggregate
    136 >>> Book.objects.all().annotate(num_authors=Count('authors__id')).aggregate(Max('pages'), Max('price'), Sum('num_authors'), Avg('num_authors'))
    137 {'num_authors__sum': 10, 'num_authors__avg': 1.66..., 'pages__max': 1132, 'price__max': Decimal("82.80")}
    138 
    139 # Bad field requests in aggregates are caught and reported
    140 >>> Book.objects.all().aggregate(num_authors=Count('foo'))
    141 Traceback (most recent call last):
    142 ...
    143 FieldError: Cannot resolve keyword 'foo' into field. Choices are: authors, contact, hardbackbook, id, isbn, name, pages, price, pubdate, publisher, rating, store
    144 
    145 >>> Book.objects.all().annotate(num_authors=Count('foo'))
    146 Traceback (most recent call last):
    147 ...
    148 FieldError: Cannot resolve keyword 'foo' into field. Choices are: authors, contact, hardbackbook, id, isbn, name, pages, price, pubdate, publisher, rating, store
    149 
    150 >>> Book.objects.all().annotate(num_authors=Count('authors__id')).aggregate(Max('foo'))
    151 Traceback (most recent call last):
    152 ...
    153 FieldError: Cannot resolve keyword 'foo' into field. Choices are: authors, contact, hardbackbook, id, isbn, name, pages, price, pubdate, publisher, rating, store, num_authors
    154 
    155 # Old-style count aggregations can be mixed with new-style
    156 >>> Book.objects.annotate(num_authors=Count('authors')).count()
    157 6
    158 
    159 # Non-ordinal, non-computed Aggregates over annotations correctly inherit
    160 # the annotation's internal type if the annotation is ordinal or computed
    161 >>> Book.objects.annotate(num_authors=Count('authors')).aggregate(Max('num_authors'))
    162 {'num_authors__max': 3}
    163 
    164 >>> Publisher.objects.annotate(avg_price=Avg('book__price')).aggregate(Max('avg_price'))
    165 {'avg_price__max': 75.0...}
    166 
    167 # Aliases are quoted to protected aliases that might be reserved names
    168 >>> Book.objects.aggregate(number=Max('pages'), select=Max('pages'))
    169 {'number': 1132, 'select': 1132}
    170 
    171 # Regression for #10064: select_related() plays nice with aggregates
    172 >>> sorted(Book.objects.select_related('publisher').annotate(num_authors=Count('authors')).values()[0].iteritems())
    173 [('contact_id', 8), ('id', 5), ('isbn', u'013790395'), ('name', u'Artificial Intelligence: A Modern Approach'), ('num_authors', 2), ('pages', 1132), ('price', Decimal("82.8...")), ('pubdate', datetime.date(1995, 1, 15)), ('publisher_id', 3), ('rating', 4.0)]
    174 
    175 # Regression for #10010: exclude on an aggregate field is correctly negated
    176 >>> len(Book.objects.annotate(num_authors=Count('authors')))
    177 6
    178 >>> len(Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=2))
    179 1
    180 >>> len(Book.objects.annotate(num_authors=Count('authors')).exclude(num_authors__gt=2))
    181 5
    182 
    183 >>> len(Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__lt=3).exclude(num_authors__lt=2))
    184 2
    185 >>> len(Book.objects.annotate(num_authors=Count('authors')).exclude(num_authors__lt=2).filter(num_authors__lt=3))
    186 2
    187 
    188 # Aggregates can be used with F() expressions
    189 # ... where the F() is pushed into the HAVING clause
    190 >>> Publisher.objects.annotate(num_books=Count('book')).filter(num_books__lt=F('num_awards')/2).order_by('name').values('name','num_books','num_awards')
    191 [{'num_books': 1, 'name': u'Morgan Kaufmann', 'num_awards': 9}, {'num_books': 2, 'name': u'Prentice Hall', 'num_awards': 7}]
    192 
    193 >>> Publisher.objects.annotate(num_books=Count('book')).exclude(num_books__lt=F('num_awards')/2).order_by('name').values('name','num_books','num_awards')
    194 [{'num_books': 2, 'name': u'Apress', 'num_awards': 3}, {'num_books': 0, 'name': u"Jonno's House of Books", 'num_awards': 0}, {'num_books': 1, 'name': u'Sams', 'num_awards': 1}]
    195 
    196 # ... and where the F() references an aggregate
    197 >>> Publisher.objects.annotate(num_books=Count('book')).filter(num_awards__gt=2*F('num_books')).order_by('name').values('name','num_books','num_awards')
    198 [{'num_books': 1, 'name': u'Morgan Kaufmann', 'num_awards': 9}, {'num_books': 2, 'name': u'Prentice Hall', 'num_awards': 7}]
    199 
    200 >>> Publisher.objects.annotate(num_books=Count('book')).exclude(num_books__lt=F('num_awards')/2).order_by('name').values('name','num_books','num_awards')
    201 [{'num_books': 2, 'name': u'Apress', 'num_awards': 3}, {'num_books': 0, 'name': u"Jonno's House of Books", 'num_awards': 0}, {'num_books': 1, 'name': u'Sams', 'num_awards': 1}]
    202 
    203 # Tests on fields with non-default table and column names.
    204 >>> Clues.objects.values('EntryID__Entry').annotate(Appearances=Count('EntryID'), Distinct_Clues=Count('Clue', distinct=True))
    205 []
    206 
    207 >>> Entries.objects.annotate(clue_count=Count('clues__ID'))
    208 []
    209 
    210 # Regression for #10089: Check handling of empty result sets with aggregates
    211 >>> Book.objects.filter(id__in=[]).count()
    212 0
    213 
    214 >>> 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'))
    215 {'max_authors': None, 'max_rating': None, 'num_authors': 0, 'avg_authors': None, 'max_price': None}
    216 
    217 >>> list(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()) == [{'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}]
    218 True
    219 
    220 # Regression for #10113 - Fields mentioned in order_by() must be included in the GROUP BY.
    221 # This only becomes a problem when the order_by introduces a new join.
    222 >>> Book.objects.annotate(num_authors=Count('authors')).order_by('publisher__name', 'name')
    223 [<Book: Practical Django Projects>, <Book: The Definitive Guide to Django: Web Development Done Right>, <Book: Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp>, <Book: Artificial Intelligence: A Modern Approach>, <Book: Python Web Development with Django>, <Book: Sams Teach Yourself Django in 24 Hours>]
    224 
    225 # Regression for #10127 - Empty select_related() works with annotate
    226 >>> books = Book.objects.all().filter(rating__lt=4.5).select_related().annotate(Avg('authors__age'))
    227 >>> sorted([(b.name, b.authors__age__avg, b.publisher.name, b.contact.name) for b in books])
    228 [(u'Artificial Intelligence: A Modern Approach', 51.5, u'Prentice Hall', u'Peter Norvig'), (u'Practical Django Projects', 29.0, u'Apress', u'James Bennett'), (u'Python Web Development with Django', 30.3..., u'Prentice Hall', u'Jeffrey Forcier'), (u'Sams Teach Yourself Django in 24 Hours', 45.0, u'Sams', u'Brad Dayley')]
    229 
    230 # Regression for #10132 - If the values() clause only mentioned extra(select=) columns, those columns are used for grouping
    231 >>> Book.objects.extra(select={'pub':'publisher_id'}).values('pub').annotate(Count('id')).order_by('pub')
    232 [{'pub': 1, 'id__count': 2}, {'pub': 2, 'id__count': 1}, {'pub': 3, 'id__count': 2}, {'pub': 4, 'id__count': 1}]
    233 
    234 >>> Book.objects.extra(select={'pub':'publisher_id','foo':'pages'}).values('pub').annotate(Count('id')).order_by('pub')
    235 [{'pub': 1, 'id__count': 2}, {'pub': 2, 'id__count': 1}, {'pub': 3, 'id__count': 2}, {'pub': 4, 'id__count': 1}]
    236 
    237 # Regression for #10182 - Queries with aggregate calls are correctly realiased when used in a subquery
    238 >>> ids = Book.objects.filter(pages__gt=100).annotate(n_authors=Count('authors')).filter(n_authors__gt=2).order_by('n_authors')
    239 >>> Book.objects.filter(id__in=ids)
    240 [<Book: Python Web Development with Django>]
    241 
    242 # Regression for #10197 -- Queries with aggregates can be pickled.
    243 # First check that pickling is possible at all. No crash = success
    244 >>> qs = Book.objects.annotate(num_authors=Count('authors'))
    245 >>> out = pickle.dumps(qs)
    246 
    247 # Then check that the round trip works.
    248 >>> query = qs.query.get_compiler(qs.db).as_sql()[0]
    249 >>> select_fields = qs.query.select_fields
    250 >>> query2 = pickle.loads(pickle.dumps(qs))
    251 >>> query2.query.get_compiler(query2.db).as_sql()[0] == query
    252 True
    253 >>> query2.query.select_fields = select_fields
    254 
    255 # Regression for #10199 - Aggregate calls clone the original query so the original query can still be used
    256 >>> books = Book.objects.all()
    257 >>> _ = books.aggregate(Avg('authors__age'))
    258 >>> books.all()
    259 [<Book: Artificial Intelligence: A Modern Approach>, <Book: Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp>, <Book: Practical Django Projects>, <Book: Python Web Development with Django>, <Book: Sams Teach Yourself Django in 24 Hours>, <Book: The Definitive Guide to Django: Web Development Done Right>]
    260 
    261 # Regression for #10248 - Annotations work with DateQuerySets
    262 >>> Book.objects.annotate(num_authors=Count('authors')).filter(num_authors=2).dates('pubdate', 'day')
    263 [datetime.datetime(1995, 1, 15, 0, 0), datetime.datetime(2007, 12, 6, 0, 0)]
    264 
    265 # Regression for #10290 - extra selects with parameters can be used for
    266 # grouping.
    267 >>> qs = Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'sheets' : '(pages + %s) / %s'}, select_params=[1, 2]).order_by('sheets').values('sheets')
    268 >>> [int(x['sheets']) for x in qs]
    269 [150, 175, 224, 264, 473, 566]
    270 
    271 # Regression for 10425 - annotations don't get in the way of a count() clause
    272 >>> Book.objects.values('publisher').annotate(Count('publisher')).count()
    273 4
    274 
    275 >>> Book.objects.annotate(Count('publisher')).values('publisher').count()
    276 6
    277 
    278 >>> publishers = Publisher.objects.filter(id__in=(1,2))
    279 >>> publishers
    280 [<Publisher: Apress>, <Publisher: Sams>]
    281 
    282 >>> publishers = publishers.annotate(n_books=models.Count('book'))
    283 >>> publishers[0].n_books
    284 2
    285 
    286 >>> publishers
    287 [<Publisher: Apress>, <Publisher: Sams>]
    288 
    289 >>> books = Book.objects.filter(publisher__in=publishers)
    290 >>> books
    291 [<Book: Practical Django Projects>, <Book: Sams Teach Yourself Django in 24 Hours>, <Book: The Definitive Guide to Django: Web Development Done Right>]
    292 
    293 >>> publishers
    294 [<Publisher: Apress>, <Publisher: Sams>]
    295 
    296 
    297 # Regression for 10666 - inherited fields work with annotations and aggregations
    298 >>> HardbackBook.objects.aggregate(n_pages=Sum('book_ptr__pages'))
    299 {'n_pages': 2078}
    300 
    301 >>> HardbackBook.objects.aggregate(n_pages=Sum('pages'))
    302 {'n_pages': 2078}
    303 
    304 >>> HardbackBook.objects.annotate(n_authors=Count('book_ptr__authors')).values('name','n_authors')
    305 [{'n_authors': 2, 'name': u'Artificial Intelligence: A Modern Approach'}, {'n_authors': 1, 'name': u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp'}]
    306 
    307 >>> HardbackBook.objects.annotate(n_authors=Count('authors')).values('name','n_authors')
    308 [{'n_authors': 2, 'name': u'Artificial Intelligence: A Modern Approach'}, {'n_authors': 1, 'name': u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp'}]
    309 
    310 # Regression for #10766 - Shouldn't be able to reference an aggregate fields in an an aggregate() call.
    311 >>> Book.objects.all().annotate(mean_age=Avg('authors__age')).annotate(Avg('mean_age'))
    312 Traceback (most recent call last):
    313 ...
    314 FieldError: Cannot compute Avg('mean_age'): 'mean_age' is an aggregate
    315 
    316 """
    317 }
    318 
    319 def run_stddev_tests():
    320     """Check to see if StdDev/Variance tests should be run.
    321 
    322     Stddev and Variance are not guaranteed to be available for SQLite, and
    323     are not available for PostgreSQL before 8.2.
    324     """
    325     if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == 'django.db.backends.sqlite3':
    326         return False
    327 
    328     class StdDevPop(object):
    329         sql_function = 'STDDEV_POP'
    330 
    331     try:
    332         connection.ops.check_aggregate_support(StdDevPop())
    333     except:
    334         return False
    335     return True
    336 
    337 if run_stddev_tests():
    338     __test__['API_TESTS'] += """
    339 >>> Book.objects.aggregate(StdDev('pages'))
    340 {'pages__stddev': 311.46...}
    341 
    342 >>> Book.objects.aggregate(StdDev('rating'))
    343 {'rating__stddev': 0.60...}
    344 
    345 >>> Book.objects.aggregate(StdDev('price'))
    346 {'price__stddev': 24.16...}
    347 
    348 
    349 >>> Book.objects.aggregate(StdDev('pages', sample=True))
    350 {'pages__stddev': 341.19...}
    351 
    352 >>> Book.objects.aggregate(StdDev('rating', sample=True))
    353 {'rating__stddev': 0.66...}
    354 
    355 >>> Book.objects.aggregate(StdDev('price', sample=True))
    356 {'price__stddev': 26.46...}
    357 
    358 
    359 >>> Book.objects.aggregate(Variance('pages'))
    360 {'pages__variance': 97010.80...}
    361 
    362 >>> Book.objects.aggregate(Variance('rating'))
    363 {'rating__variance': 0.36...}
    364 
    365 >>> Book.objects.aggregate(Variance('price'))
    366 {'price__variance': 583.77...}
    367 
    368 
    369 >>> Book.objects.aggregate(Variance('pages', sample=True))
    370 {'pages__variance': 116412.96...}
    371 
    372 >>> Book.objects.aggregate(Variance('rating', sample=True))
    373 {'rating__variance': 0.44...}
    374 
    375 >>> Book.objects.aggregate(Variance('price', sample=True))
    376 {'price__variance': 700.53...}
    377 
    378 """
  • tests/regressiontests/aggregation_regress/tests.py

    diff --git a/tests/regressiontests/aggregation_regress/tests.py b/tests/regressiontests/aggregation_regress/tests.py
    index 3c4bdfa..6f33481 100644
    a b  
     1import datetime
     2from decimal import Decimal
     3
     4from django.core.exceptions import FieldError
    15from django.conf import settings
    2 from django.test import TestCase
     6from django.test import TestCase, Approximate
    37from django.db import DEFAULT_DB_ALIAS
    4 from django.db.models import Count, Max
     8from django.db.models import Count, Max, Avg, Sum, F
    59
    610from regressiontests.aggregation_regress.models import *
    711
    812
    9 class AggregationTests(TestCase):
     13def run_stddev_tests():
     14    """Check to see if StdDev/Variance tests should be run.
     15
     16    Stddev and Variance are not guaranteed to be available for SQLite, and
     17    are not available for PostgreSQL before 8.2.
     18    """
     19    if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == 'django.db.backends.sqlite3':
     20        return False
     21
     22    class StdDevPop(object):
     23        sql_function = 'STDDEV_POP'
    1024
     25    try:
     26        connection.ops.check_aggregate_support(StdDevPop())
     27    except:
     28        return False
     29    return True
     30
     31
     32class AggregationTests(TestCase):
     33    def assertObjectAttrs(self, obj, **kwargs):
     34        for attr, value in kwargs.iteritems():
     35            self.assertEqual(getattr(obj, attr), value)
     36   
    1137    def test_aggregates_in_where_clause(self):
    1238        """
    1339        Regression test for #12822: DatabaseError: aggregates not allowed in
    class AggregationTests(TestCase):  
    7096            }).annotate(total_books=Count('book'))
    7197            # force execution of the query
    7298            list(qs)
     99   
     100    def test_aggregate(self):
     101        # Ordering requests are ignored
     102        self.assertEqual(
     103            Author.objects.order_by("name").aggregate(Avg("age")),
     104            {"age__avg": Approximate(37.444, places=1)}
     105        )
     106       
     107        # Implicit ordering is also ignored
     108        self.assertEqual(
     109            Book.objects.aggregate(Sum("pages")),
     110            {"pages__sum": 3703},
     111        )
     112       
     113        # Baseline results
     114        self.assertEqual(
     115            Book.objects.aggregate(Sum('pages'), Avg('pages')),
     116            {'pages__sum': 3703, 'pages__avg': Approximate(617.166, places=2)}       
     117        )
     118       
     119        # Empty values query doesn't affect grouping or results
     120        self.assertEqual(
     121            Book.objects.values().aggregate(Sum('pages'), Avg('pages')),
     122            {'pages__sum': 3703, 'pages__avg': Approximate(617.166, places=2)}
     123        )
     124
     125        # Aggregate overrides extra selected column
     126        self.assertEqual(
     127            Book.objects.extra(select={'price_per_page' : 'price / pages'}).aggregate(Sum('pages')),
     128            {'pages__sum': 3703}
     129        )
     130   
     131    def test_annotation(self):
     132        # Annotations get combined with extra select clauses
     133        obj = Book.objects.annotate(mean_auth_age=Avg("authors__age")).extra(select={"manufacture_cost": "price * .5"}).get(pk=2)
     134        self.assertObjectAttrs(obj,
     135            contact_id=3,
     136            id=2,
     137            isbn=u'067232959',
     138            manufacture_cost=11.545,
     139            mean_auth_age=45.0,
     140            name='Sams Teach Yourself Django in 24 Hours',
     141            pages=528,
     142            price=Decimal("23.09"),
     143            pubdate=datetime.date(2008, 3, 3),
     144            publisher_id=2,
     145            rating=3.0
     146        )
     147
     148        # Order of the annotate/extra in the query doesn't matter
     149        obj = Book.objects.extra(select={'manufacture_cost' : 'price * .5'}).annotate(mean_auth_age=Avg('authors__age')).get(pk=2)
     150        self.assertObjectAttrs(obj,
     151            contact_id=3,
     152            id=2,
     153            isbn=u'067232959',
     154            manufacture_cost=11.545,
     155            mean_auth_age=45.0,
     156            name=u'Sams Teach Yourself Django in 24 Hours',
     157            pages=528,
     158            price=Decimal("23.09"),
     159            pubdate=datetime.date(2008, 3, 3),
     160            publisher_id=2,
     161            rating=3.0
     162        )
     163
     164        # Values queries can be combined with annotate and extra
     165        obj = Book.objects.annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).values().get(pk=2)
     166        self.assertEqual(obj, {
     167            "contact_id": 3,
     168            "id": 2,
     169            "isbn": u"067232959",
     170            "manufacture_cost": 11.545,
     171            "mean_auth_age": 45.0,
     172            "name": u"Sams Teach Yourself Django in 24 Hours",
     173            "pages": 528,
     174            "price": Decimal("23.09"),
     175            "pubdate": datetime.date(2008, 3, 3),
     176            "publisher_id": 2,
     177            "rating": 3.0,
     178        })
     179       
     180        # The order of the (empty) values, annotate and extra clauses doesn't
     181        # matter
     182        obj = Book.objects.values().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).get(pk=2)
     183        self.assertEqual(obj, {
     184            'contact_id': 3,
     185            'id': 2,
     186            'isbn': u'067232959',
     187            'manufacture_cost': 11.545,
     188            'mean_auth_age': 45.0,
     189            'name': u'Sams Teach Yourself Django in 24 Hours',
     190            'pages': 528,
     191            'price': Decimal("23.09"),
     192            'pubdate': datetime.date(2008, 3, 3),
     193            'publisher_id': 2,
     194            'rating': 3.0
     195        })
     196
     197        # If the annotation precedes the values clause, it won't be included
     198        # unless it is explicitly named
     199        obj = Book.objects.annotate(mean_auth_age=Avg('authors__age')).extra(select={'price_per_page' : 'price / pages'}).values('name').get(pk=1)
     200        self.assertEqual(obj, {
     201            "name": u'The Definitive Guide to Django: Web Development Done Right',
     202        })
     203
     204        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)
     205        self.assertEqual(obj, {
     206            'mean_auth_age': 34.5,
     207            'name': u'The Definitive Guide to Django: Web Development Done Right',
     208        })
     209
     210        # If an annotation isn't included in the values, it can still be used
     211        # in a filter
     212        qs = Book.objects.annotate(n_authors=Count('authors')).values('name').filter(n_authors__gt=2)
     213        self.assertQuerysetEqual(
     214            qs, [
     215                {"name": u'Python Web Development with Django'}
     216            ],
     217            lambda b: b,
     218        )
     219       
     220        # The annotations are added to values output if values() precedes
     221        # annotate()
     222        obj = Book.objects.values('name').annotate(mean_auth_age=Avg('authors__age')).extra(select={'price_per_page' : 'price / pages'}).get(pk=1)
     223        self.assertEqual(obj, {
     224            'mean_auth_age': 34.5,
     225            'name': u'The Definitive Guide to Django: Web Development Done Right',
     226        })
     227       
     228        # Check that all of the objects are getting counted (allow_nulls) and
     229        # that values respects the amount of objects
     230        self.assertEqual(
     231            len(Author.objects.annotate(Avg('friends__age')).values()),
     232            9
     233        )
     234       
     235        # Check that consecutive calls to annotate accumulate in the query
     236        qs = Book.objects.values('price').annotate(oldest=Max('authors__age')).order_by('oldest', 'price').annotate(Max('publisher__num_awards'))
     237        self.assertQuerysetEqual(
     238            qs, [
     239                {'price': Decimal("30"), 'oldest': 35, 'publisher__num_awards__max': 3},
     240                {'price': Decimal("29.69"), 'oldest': 37, 'publisher__num_awards__max': 7},
     241                {'price': Decimal("23.09"), 'oldest': 45, 'publisher__num_awards__max': 1},
     242                {'price': Decimal("75"), 'oldest': 57, 'publisher__num_awards__max': 9},
     243                {'price': Decimal("82.8"), 'oldest': 57, 'publisher__num_awards__max': 7}
     244            ],
     245            lambda b: b,
     246        )
     247   
     248    def test_aggrate_annotation(self):
     249        # Aggregates can be composed over annotations.
     250        # The return type is derived from the composed aggregate
     251        vals = Book.objects.all().annotate(num_authors=Count('authors__id')).aggregate(Max('pages'), Max('price'), Sum('num_authors'), Avg('num_authors'))
     252        self.assertEqual(vals, {
     253            'num_authors__sum': 10,
     254            'num_authors__avg': Approximate(1.666, places=2),
     255            'pages__max': 1132,
     256            'price__max': Decimal("82.80")
     257        })
     258   
     259    def test_field_error(self):
     260        # Bad field requests in aggregates are caught and reported
     261        self.assertRaises(
     262            FieldError,
     263            lambda: Book.objects.all().aggregate(num_authors=Count('foo'))
     264        )
     265       
     266        self.assertRaises(
     267            FieldError,
     268            lambda: Book.objects.all().annotate(num_authors=Count('foo'))
     269        )
     270       
     271        self.assertRaises(
     272            FieldError,
     273            lambda: Book.objects.all().annotate(num_authors=Count('authors__id')).aggregate(Max('foo'))
     274        )
     275   
     276    def test_more(self):
     277        # Old-style count aggregations can be mixed with new-style
     278        self.assertEqual(
     279            Book.objects.annotate(num_authors=Count('authors')).count(),
     280            6
     281        )
     282
     283        # Non-ordinal, non-computed Aggregates over annotations correctly
     284        # inherit the annotation's internal type if the annotation is ordinal
     285        # or computed
     286        vals = Book.objects.annotate(num_authors=Count('authors')).aggregate(Max('num_authors'))
     287        self.assertEqual(
     288            vals,
     289            {'num_authors__max': 3}
     290        )
     291       
     292        vals = Publisher.objects.annotate(avg_price=Avg('book__price')).aggregate(Max('avg_price'))
     293        self.assertEqual(
     294            vals,
     295            {'avg_price__max': 75.0}
     296        )
     297       
     298        # Aliases are quoted to protected aliases that might be reserved names
     299        vals = Book.objects.aggregate(number=Max('pages'), select=Max('pages'))
     300        self.assertEqual(
     301            vals,
     302            {'number': 1132, 'select': 1132}
     303        )
     304       
     305        # Regression for #10064: select_related() plays nice with aggregates
     306        obj = Book.objects.select_related('publisher').annotate(num_authors=Count('authors')).values()[0]
     307        self.assertEqual(obj, {
     308            'contact_id': 8,
     309            'id': 5,
     310            'isbn': u'013790395',
     311            'name': u'Artificial Intelligence: A Modern Approach',
     312            'num_authors': 2,
     313            'pages': 1132,
     314            'price': Decimal("82.8"),
     315            'pubdate': datetime.date(1995, 1, 15),
     316            'publisher_id': 3,
     317            'rating': 4.0,
     318        })
     319       
     320        # Regression for #10010: exclude on an aggregate field is correctly
     321        # negated
     322        self.assertEqual(
     323            len(Book.objects.annotate(num_authors=Count('authors'))),
     324            6
     325        )
     326        self.assertEqual(
     327            len(Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=2)),
     328            1
     329        )
     330        self.assertEqual(
     331            len(Book.objects.annotate(num_authors=Count('authors')).exclude(num_authors__gt=2)),
     332            5
     333        )
     334       
     335        self.assertEqual(
     336            len(Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__lt=3).exclude(num_authors__lt=2)),
     337            2
     338        )
     339        self.assertEqual(
     340            len(Book.objects.annotate(num_authors=Count('authors')).exclude(num_authors__lt=2).filter(num_authors__lt=3)),
     341            2
     342        )
     343   
     344    def test_aggregate_fexpr(self):
     345        # Aggregates can be used with F() expressions
     346        # ... where the F() is pushed into the HAVING clause
     347        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')
     348        self.assertQuerysetEqual(
     349            qs, [
     350                {'num_books': 1, 'name': u'Morgan Kaufmann', 'num_awards': 9},
     351                {'num_books': 2, 'name': u'Prentice Hall', 'num_awards': 7}
     352            ],
     353            lambda p: p,
     354        )
     355       
     356        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')
     357        self.assertQuerysetEqual(
     358            qs, [
     359                {'num_books': 2, 'name': u'Apress', 'num_awards': 3},
     360                {'num_books': 0, 'name': u"Jonno's House of Books", 'num_awards': 0},
     361                {'num_books': 1, 'name': u'Sams', 'num_awards': 1}
     362            ],
     363            lambda p: p,
     364        )
     365       
     366        # ... and where the F() references an aggregate
     367        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')
     368        self.assertQuerysetEqual(
     369            qs, [
     370                {'num_books': 1, 'name': u'Morgan Kaufmann', 'num_awards': 9},
     371                {'num_books': 2, 'name': u'Prentice Hall', 'num_awards': 7}
     372            ],
     373            lambda p: p,
     374        )
     375       
     376        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')
     377        self.assertQuerysetEqual(
     378            qs, [
     379                {'num_books': 2, 'name': u'Apress', 'num_awards': 3},
     380                {'num_books': 0, 'name': u"Jonno's House of Books", 'num_awards': 0},
     381                {'num_books': 1, 'name': u'Sams', 'num_awards': 1}
     382            ],
     383            lambda p: p,
     384        )
     385   
     386    def test_db_col_table(self):
     387        # Tests on fields with non-default table and column names.
     388        qs = Clues.objects.values('EntryID__Entry').annotate(Appearances=Count('EntryID'), Distinct_Clues=Count('Clue', distinct=True))
     389        self.assertQuerysetEqual(qs, [])
     390       
     391        qs = Entries.objects.annotate(clue_count=Count('clues__ID'))
     392        self.assertQuerysetEqual(qs, [])
     393   
     394    def test_empty(self):
     395        # Regression for #10089: Check handling of empty result sets with
     396        # aggregates
     397        self.assertEqual(
     398            Book.objects.filter(id__in=[]).count(),
     399            0
     400        )
     401       
     402        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'))
     403        self.assertEqual(
     404            vals,
     405            {'max_authors': None, 'max_rating': None, 'num_authors': 0, 'avg_authors': None, 'max_price': None}
     406        )
     407       
     408        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()
     409        self.assertQuerysetEqual(
     410            qs, [
     411                {'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}
     412            ],
     413            lambda p: p
     414        )
     415   
     416    def test_more_more(self):
     417        # Regression for #10113 - Fields mentioned in order_by() must be
     418        # included in the GROUP BY. This only becomes a problem when the
     419        # order_by introduces a new join.
     420        self.assertQuerysetEqual(
     421            Book.objects.annotate(num_authors=Count('authors')).order_by('publisher__name', 'name'), [
     422                "Practical Django Projects",
     423                "The Definitive Guide to Django: Web Development Done Right",
     424                "Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp",
     425                "Artificial Intelligence: A Modern Approach",
     426                "Python Web Development with Django",
     427                "Sams Teach Yourself Django in 24 Hours",
     428            ],
     429            lambda b: b.name
     430        )
     431       
     432        # Regression for #10127 - Empty select_related() works with annotate
     433        qs = Book.objects.filter(rating__lt=4.5).select_related().annotate(Avg('authors__age'))
     434        self.assertQuerysetEqual(
     435            qs, [
     436                (u'Artificial Intelligence: A Modern Approach', 51.5, u'Prentice Hall', u'Peter Norvig'),
     437                (u'Practical Django Projects', 29.0, u'Apress', u'James Bennett'),
     438                (u'Python Web Development with Django', Approximate(30.333, places=2), u'Prentice Hall', u'Jeffrey Forcier'),
     439                (u'Sams Teach Yourself Django in 24 Hours', 45.0, u'Sams', u'Brad Dayley')
     440            ],
     441            lambda b: (b.name, b.authors__age__avg, b.publisher.name, b.contact.name)
     442        )
     443       
     444        # Regression for #10132 - If the values() clause only mentioned extra
     445        # (select=) columns, those columns are used for grouping
     446        qs = Book.objects.extra(select={'pub':'publisher_id'}).values('pub').annotate(Count('id')).order_by('pub')
     447        self.assertQuerysetEqual(
     448            qs, [
     449                {'pub': 1, 'id__count': 2},
     450                {'pub': 2, 'id__count': 1},
     451                {'pub': 3, 'id__count': 2},
     452                {'pub': 4, 'id__count': 1}
     453            ],
     454            lambda b: b
     455        )
     456       
     457        qs = Book.objects.extra(select={'pub':'publisher_id', 'foo':'pages'}).values('pub').annotate(Count('id')).order_by('pub')
     458        self.assertQuerysetEqual(
     459            qs, [
     460                {'pub': 1, 'id__count': 2},
     461                {'pub': 2, 'id__count': 1},
     462                {'pub': 3, 'id__count': 2},
     463                {'pub': 4, 'id__count': 1}
     464            ],
     465            lambda b: b
     466        )
     467       
     468        # Regression for #10182 - Queries with aggregate calls are correctly
     469        # realiased when used in a subquery
     470        ids = Book.objects.filter(pages__gt=100).annotate(n_authors=Count('authors')).filter(n_authors__gt=2).order_by('n_authors')
     471        self.assertQuerysetEqual(
     472            Book.objects.filter(id__in=ids), [
     473                "Python Web Development with Django",
     474            ],
     475            lambda b: b.name
     476        )
     477   
     478    def test_pickle(self):
     479        # Regression for #10197 -- Queries with aggregates can be pickled.
     480        # First check that pickling is possible at all. No crash = success
     481        qs = Book.objects.annotate(num_authors=Count('authors'))
     482        out = pickle.dumps(qs)
     483
     484        # Then check that the round trip works.
     485        query = qs.query.get_compiler(qs.db).as_sql()[0]
     486        qs2 = pickle.loads(pickle.dumps(qs))
     487        self.assertEqual(
     488            qs2.query.get_compiler(qs2.db).as_sql()[0],
     489            query,
     490        )
     491   
     492    def test_more_more_more(self):
     493        # Regression for #10199 - Aggregate calls clone the original query so
     494        # the original query can still be used
     495        books = Book.objects.all()
     496        books.aggregate(Avg("authors__age"))
     497        self.assertQuerysetEqual(
     498            books.all(), [
     499                u'Artificial Intelligence: A Modern Approach',
     500                u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp',
     501                u'Practical Django Projects',
     502                u'Python Web Development with Django',
     503                u'Sams Teach Yourself Django in 24 Hours',
     504                u'The Definitive Guide to Django: Web Development Done Right'
     505            ],
     506            lambda b: b.name
     507        )
     508       
     509        # Regression for #10248 - Annotations work with DateQuerySets
     510        qs = Book.objects.annotate(num_authors=Count('authors')).filter(num_authors=2).dates('pubdate', 'day')
     511        self.assertQuerysetEqual(
     512            qs, [
     513                datetime.datetime(1995, 1, 15, 0, 0),
     514                datetime.datetime(2007, 12, 6, 0, 0)
     515            ],
     516            lambda b: b
     517        )
     518       
     519        # Regression for #10290 - extra selects with parameters can be used for
     520        # grouping.
     521        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')
     522        self.assertQuerysetEqual(
     523            qs, [
     524                150,
     525                175,
     526                224,
     527                264,
     528                473,
     529                566
     530            ],
     531            lambda b: int(b["sheets"])
     532        )
     533       
     534        # Regression for 10425 - annotations don't get in the way of a count()
     535        # clause
     536        self.assertEqual(
     537            Book.objects.values('publisher').annotate(Count('publisher')).count(),
     538            4
     539        )
     540        self.assertEqual(
     541            Book.objects.annotate(Count('publisher')).values('publisher').count(),
     542            6
     543        )
     544       
     545        publishers = Publisher.objects.filter(id__in=[1, 2])
     546        self.assertQuerysetEqual(
     547            publishers, [
     548                "Apress",
     549                "Sams"
     550            ],
     551            lambda p: p.name
     552        )
     553       
     554        publishers = publishers.annotate(n_books=Count("book"))
     555        self.assertEqual(
     556            publishers[0].n_books,
     557            2
     558        )
     559       
     560        self.assertQuerysetEqual(
     561            publishers, [
     562                "Apress",
     563                "Sams",
     564            ],
     565            lambda p: p.name
     566        )
     567       
     568        books = Book.objects.filter(publisher__in=publishers)
     569        self.assertQuerysetEqual(
     570            books, [
     571                "Practical Django Projects",
     572                "Sams Teach Yourself Django in 24 Hours",
     573                "The Definitive Guide to Django: Web Development Done Right",
     574            ],
     575            lambda b: b.name
     576        )
     577        self.assertQuerysetEqual(
     578            publishers, [
     579                "Apress",
     580                "Sams",
     581            ],
     582            lambda p: p.name
     583        )
     584       
     585        # Regression for 10666 - inherited fields work with annotations and
     586        # aggregations
     587        self.assertEqual(
     588            HardbackBook.objects.aggregate(n_pages=Sum('book_ptr__pages')),
     589            {'n_pages': 2078}
     590        )
     591       
     592        self.assertEqual(
     593            HardbackBook.objects.aggregate(n_pages=Sum('pages')),
     594            {'n_pages': 2078},
     595        )
     596       
     597        qs = HardbackBook.objects.annotate(n_authors=Count('book_ptr__authors')).values('name', 'n_authors')
     598        self.assertQuerysetEqual(
     599            qs, [
     600                {'n_authors': 2, 'name': u'Artificial Intelligence: A Modern Approach'},
     601                {'n_authors': 1, 'name': u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp'}
     602            ],
     603            lambda h: h
     604        )
     605       
     606        qs = HardbackBook.objects.annotate(n_authors=Count('authors')).values('name', 'n_authors')
     607        self.assertQuerysetEqual(
     608            qs, [
     609                {'n_authors': 2, 'name': u'Artificial Intelligence: A Modern Approach'},
     610                {'n_authors': 1, 'name': u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp'}
     611            ],
     612            lambda h: h,
     613        )
     614       
     615        # Regression for #10766 - Shouldn't be able to reference an aggregate
     616        # fields in an an aggregate() call.
     617        self.assertRaises(
     618            FieldError,
     619            lambda: Book.objects.annotate(mean_age=Avg('authors__age')).annotate(Avg('mean_age'))
     620        )
     621   
     622    if run_stddev_tests():
     623        def test_stddev(self):
     624            self.assertEqual(
     625                Book.objects.aggregate(StdDev('pages')),
     626                {'pages__stddev': 311.46}
     627            )
     628           
     629            self.assertEqual(
     630                Book.objects.aggregate(StdDev('rating')),
     631                {'rating__stddev': 0.60}
     632            )
     633           
     634            self.assertEqual(
     635                Book.objects.aggregate(StdDev('price')),
     636                {'price__stddev': 24.16}
     637            )
     638           
     639            self.assertEqual(
     640                Book.objects.aggregate(StdDev('pages', sample=True)),
     641                {'pages__stddev': 341.19}
     642            )
     643           
     644            self.assertEqual(
     645                Book.objects.aggregate(StdDev('rating', sample=True)),
     646                {'rating__stddev': 0.66}
     647            )
     648           
     649            self.assertEqual(
     650                Book.objects.aggregate(StdDev('price', sample=True)),
     651                {'price__stddev': 26.46}
     652            )
     653           
     654            self.assertEqual(
     655                Book.objects.aggregate(Variance('pages')),
     656                {'pages__variance': 97010.80}
     657            )
     658           
     659            self.assertEqual(
     660                Book.objects.aggregate(Variance('rating')),
     661                {'rating__variance': 0.36}
     662            )
     663           
     664            self.assertEqual(
     665                Book.objects.aggregate(Variance('price')),
     666                {'price__variance': 583.77}
     667            )
     668           
     669            self.assertEqual(
     670                Book.objects.aggregate(Variance('pages', sample=True)),
     671                {'pages__variance': 116412.96}
     672            )
     673           
     674            self.assertEqual(
     675                Book.objects.aggregate(Variance('rating', sample=True)),
     676                {'rating__variance': 0.44}
     677            )
     678           
     679            self.assertEqual(
     680                Book.objects.aggregate(Variance('price', sample=True)),
     681                {'price__variance': 700.53}
     682            )
Back to Top