1
from __future__ import absolute_import
1
from __future__ import unicode_literals
4
4
from decimal import Decimal
7
from django.db import connection
6
8
from django.db.models import Avg, Sum, Count, Max, Min
7
from django.test import TestCase, Approximate
9
from django.test import TestCase
10
from django.test.utils import Approximate
11
from django.test.utils import CaptureQueriesContext
9
13
from .models import Author, Publisher, Book, Store
439
443
vals = Author.objects.filter(pk=1).aggregate(Count("friends__id"))
440
444
self.assertEqual(vals, {"friends__id__count": 2})
442
books = Book.objects.annotate(num_authors=Count("authors__name")).filter(num_authors__ge=2).order_by("pk")
446
books = Book.objects.annotate(num_authors=Count("authors__name")).filter(num_authors__exact=2).order_by("pk")
443
447
self.assertQuerysetEqual(
445
449
"The Definitive Guide to Django: Web Development Done Right",
585
589
"datetime.date(2008, 1, 1)"
593
def test_values_aggregation(self):
595
max_rating = Book.objects.values('rating').aggregate(max_rating=Max('rating'))
596
self.assertEqual(max_rating['max_rating'], 5)
597
max_books_per_rating = Book.objects.values('rating').annotate(
598
books_per_rating=Count('id')
599
).aggregate(Max('books_per_rating'))
601
max_books_per_rating,
602
{'books_per_rating__max': 3})
604
def test_ticket17424(self):
606
Check that doing exclude() on a foreign model after annotate()
609
all_books = list(Book.objects.values_list('pk', flat=True).order_by('pk'))
610
annotated_books = Book.objects.order_by('pk').annotate(one=Count("id"))
612
# The value doesn't matter, we just need any negative
613
# constraint on a related model that's a noop.
614
excluded_books = annotated_books.exclude(publisher__name="__UNLIKELY_VALUE__")
616
# Try to generate query tree
617
str(excluded_books.query)
619
self.assertQuerysetEqual(excluded_books, all_books, lambda x: x.pk)
621
# Check internal state
622
self.assertIsNone(annotated_books.query.alias_map["aggregation_book"].join_type)
623
self.assertIsNone(excluded_books.query.alias_map["aggregation_book"].join_type)
625
def test_ticket12886(self):
627
Check that aggregation over sliced queryset works correctly.
629
qs = Book.objects.all().order_by('-rating')[0:3]
630
vals = qs.aggregate(average_top3_rating=Avg('rating'))['average_top3_rating']
631
self.assertAlmostEqual(vals, 4.5, places=2)
633
def test_ticket11881(self):
635
Check that subqueries do not needlessly contain ORDER BY, SELECT FOR UPDATE
636
or select_related() stuff.
638
qs = Book.objects.all().select_for_update().order_by(
639
'pk').select_related('publisher').annotate(max_pk=Max('pk'))
640
with CaptureQueriesContext(connection) as captured_queries:
641
qs.aggregate(avg_pk=Avg('max_pk'))
642
self.assertEqual(len(captured_queries), 1)
643
qstr = captured_queries[0]['sql'].lower()
644
self.assertNotIn('for update', qstr)
645
forced_ordering = connection.ops.force_no_ordering()
647
# If the backend needs to force an ordering we make sure it's
648
# the only "ORDER BY" clause present in the query.
650
re.findall(r'order by (\w+)', qstr),
651
[', '.join(forced_ordering).lower()]
654
self.assertNotIn('order by', qstr)
655
self.assertEqual(qstr.count(' join '), 0)