summaryrefslogtreecommitdiff
path: root/tests/aggregation_regress
diff options
context:
space:
mode:
authordjango-bot <ops@djangoproject.com>2022-02-03 20:24:19 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-02-07 20:37:05 +0100
commit9c19aff7c7561e3a82978a272ecdaad40dda5c00 (patch)
treef0506b668a013d0063e5fba3dbf4863b466713ba /tests/aggregation_regress
parentf68fa8b45dfac545cfc4111d4e52804c86db68d3 (diff)
downloaddjango-9c19aff7c7561e3a82978a272ecdaad40dda5c00.tar.gz
Refs #33476 -- Reformatted code with Black.
Diffstat (limited to 'tests/aggregation_regress')
-rw-r--r--tests/aggregation_regress/models.py22
-rw-r--r--tests/aggregation_regress/tests.py1683
2 files changed, 975 insertions, 730 deletions
diff --git a/tests/aggregation_regress/models.py b/tests/aggregation_regress/models.py
index f7b42a229e..2687cb4840 100644
--- a/tests/aggregation_regress/models.py
+++ b/tests/aggregation_regress/models.py
@@ -1,6 +1,4 @@
-from django.contrib.contenttypes.fields import (
- GenericForeignKey, GenericRelation,
-)
+from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models
@@ -8,7 +6,7 @@ from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
- friends = models.ManyToManyField('self', blank=True)
+ friends = models.ManyToManyField("self", blank=True)
class Publisher(models.Model):
@@ -20,7 +18,7 @@ class ItemTag(models.Model):
tag = models.CharField(max_length=100)
content_type = models.ForeignKey(ContentType, models.CASCADE)
object_id = models.PositiveIntegerField()
- content_object = GenericForeignKey('content_type', 'object_id')
+ content_object = GenericForeignKey("content_type", "object_id")
class Book(models.Model):
@@ -30,13 +28,13 @@ class Book(models.Model):
rating = models.FloatField()
price = models.DecimalField(decimal_places=2, max_digits=6)
authors = models.ManyToManyField(Author)
- contact = models.ForeignKey(Author, models.CASCADE, related_name='book_contact_set')
+ contact = models.ForeignKey(Author, models.CASCADE, related_name="book_contact_set")
publisher = models.ForeignKey(Publisher, models.CASCADE)
pubdate = models.DateField()
tags = GenericRelation(ItemTag)
class Meta:
- ordering = ('name',)
+ ordering = ("name",)
class Store(models.Model):
@@ -47,14 +45,16 @@ class Store(models.Model):
class Entries(models.Model):
- EntryID = models.AutoField(primary_key=True, db_column='Entry ID')
+ EntryID = models.AutoField(primary_key=True, db_column="Entry ID")
Entry = models.CharField(unique=True, max_length=50)
Exclude = models.BooleanField(default=False)
class Clues(models.Model):
ID = models.AutoField(primary_key=True)
- EntryID = models.ForeignKey(Entries, models.CASCADE, verbose_name='Entry', db_column='Entry ID')
+ EntryID = models.ForeignKey(
+ Entries, models.CASCADE, verbose_name="Entry", db_column="Entry ID"
+ )
Clue = models.CharField(max_length=150)
@@ -86,4 +86,6 @@ class Charlie(models.Model):
class SelfRefFK(models.Model):
name = models.CharField(max_length=50)
- parent = models.ForeignKey('self', models.SET_NULL, null=True, blank=True, related_name='children')
+ parent = models.ForeignKey(
+ "self", models.SET_NULL, null=True, blank=True, related_name="children"
+ )
diff --git a/tests/aggregation_regress/tests.py b/tests/aggregation_regress/tests.py
index 5d841b166a..ae7e4a0be5 100644
--- a/tests/aggregation_regress/tests.py
+++ b/tests/aggregation_regress/tests.py
@@ -8,31 +8,53 @@ from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import FieldError
from django.db import connection
from django.db.models import (
- Aggregate, Avg, Case, Count, DecimalField, F, IntegerField, Max, Q, StdDev,
- Sum, Value, Variance, When,
+ Aggregate,
+ Avg,
+ Case,
+ Count,
+ DecimalField,
+ F,
+ IntegerField,
+ Max,
+ Q,
+ StdDev,
+ Sum,
+ Value,
+ Variance,
+ When,
)
from django.test import TestCase, skipUnlessAnyDBFeature, skipUnlessDBFeature
from django.test.utils import Approximate
from .models import (
- Alfa, Author, Book, Bravo, Charlie, Clues, Entries, HardbackBook, ItemTag,
- Publisher, SelfRefFK, Store, WithManualPK,
+ Alfa,
+ Author,
+ Book,
+ Bravo,
+ Charlie,
+ Clues,
+ Entries,
+ HardbackBook,
+ ItemTag,
+ Publisher,
+ SelfRefFK,
+ Store,
+ WithManualPK,
)
class AggregationTests(TestCase):
-
@classmethod
def setUpTestData(cls):
- cls.a1 = Author.objects.create(name='Adrian Holovaty', age=34)
- cls.a2 = Author.objects.create(name='Jacob Kaplan-Moss', age=35)
- cls.a3 = Author.objects.create(name='Brad Dayley', age=45)
- cls.a4 = Author.objects.create(name='James Bennett', age=29)
- cls.a5 = Author.objects.create(name='Jeffrey Forcier', age=37)
- cls.a6 = Author.objects.create(name='Paul Bissex', age=29)
- cls.a7 = Author.objects.create(name='Wesley J. Chun', age=25)
- cls.a8 = Author.objects.create(name='Peter Norvig', age=57)
- cls.a9 = Author.objects.create(name='Stuart Russell', age=46)
+ cls.a1 = Author.objects.create(name="Adrian Holovaty", age=34)
+ cls.a2 = Author.objects.create(name="Jacob Kaplan-Moss", age=35)
+ cls.a3 = Author.objects.create(name="Brad Dayley", age=45)
+ cls.a4 = Author.objects.create(name="James Bennett", age=29)
+ cls.a5 = Author.objects.create(name="Jeffrey Forcier", age=37)
+ cls.a6 = Author.objects.create(name="Paul Bissex", age=29)
+ cls.a7 = Author.objects.create(name="Wesley J. Chun", age=25)
+ cls.a8 = Author.objects.create(name="Peter Norvig", age=57)
+ cls.a9 = Author.objects.create(name="Stuart Russell", age=46)
cls.a1.friends.add(cls.a2, cls.a4)
cls.a2.friends.add(cls.a1, cls.a7)
cls.a4.friends.add(cls.a1)
@@ -42,40 +64,74 @@ class AggregationTests(TestCase):
cls.a8.friends.add(cls.a9)
cls.a9.friends.add(cls.a8)
- cls.p1 = Publisher.objects.create(name='Apress', num_awards=3)
- cls.p2 = Publisher.objects.create(name='Sams', num_awards=1)
- cls.p3 = Publisher.objects.create(name='Prentice Hall', num_awards=7)
- cls.p4 = Publisher.objects.create(name='Morgan Kaufmann', num_awards=9)
+ cls.p1 = Publisher.objects.create(name="Apress", num_awards=3)
+ cls.p2 = Publisher.objects.create(name="Sams", num_awards=1)
+ cls.p3 = Publisher.objects.create(name="Prentice Hall", num_awards=7)
+ cls.p4 = Publisher.objects.create(name="Morgan Kaufmann", num_awards=9)
cls.p5 = Publisher.objects.create(name="Jonno's House of Books", num_awards=0)
cls.b1 = Book.objects.create(
- isbn='159059725', name='The Definitive Guide to Django: Web Development Done Right',
- pages=447, rating=4.5, price=Decimal('30.00'), contact=cls.a1, publisher=cls.p1,
- pubdate=datetime.date(2007, 12, 6)
+ isbn="159059725",
+ name="The Definitive Guide to Django: Web Development Done Right",
+ pages=447,
+ rating=4.5,
+ price=Decimal("30.00"),
+ contact=cls.a1,
+ publisher=cls.p1,
+ pubdate=datetime.date(2007, 12, 6),
)
cls.b2 = Book.objects.create(
- isbn='067232959', name='Sams Teach Yourself Django in 24 Hours',
- pages=528, rating=3.0, price=Decimal('23.09'), contact=cls.a3, publisher=cls.p2,
- pubdate=datetime.date(2008, 3, 3)
+ isbn="067232959",
+ name="Sams Teach Yourself Django in 24 Hours",
+ pages=528,
+ rating=3.0,
+ price=Decimal("23.09"),
+ contact=cls.a3,
+ publisher=cls.p2,
+ pubdate=datetime.date(2008, 3, 3),
)
cls.b3 = Book.objects.create(
- isbn='159059996', name='Practical Django Projects',
- pages=300, rating=4.0, price=Decimal('29.69'), contact=cls.a4, publisher=cls.p1,
- pubdate=datetime.date(2008, 6, 23)
+ isbn="159059996",
+ name="Practical Django Projects",
+ pages=300,
+ rating=4.0,
+ price=Decimal("29.69"),
+ contact=cls.a4,
+ publisher=cls.p1,
+ pubdate=datetime.date(2008, 6, 23),
)
cls.b4 = Book.objects.create(
- isbn='013235613', name='Python Web Development with Django',
- pages=350, rating=4.0, price=Decimal('29.69'), contact=cls.a5, publisher=cls.p3,
- pubdate=datetime.date(2008, 11, 3)
+ isbn="013235613",
+ name="Python Web Development with Django",
+ pages=350,
+ rating=4.0,
+ price=Decimal("29.69"),
+ contact=cls.a5,
+ publisher=cls.p3,
+ pubdate=datetime.date(2008, 11, 3),
)
cls.b5 = HardbackBook.objects.create(
- isbn='013790395', name='Artificial Intelligence: A Modern Approach',
- pages=1132, rating=4.0, price=Decimal('82.80'), contact=cls.a8, publisher=cls.p3,
- pubdate=datetime.date(1995, 1, 15), weight=4.5)
+ isbn="013790395",
+ name="Artificial Intelligence: A Modern Approach",
+ pages=1132,
+ rating=4.0,
+ price=Decimal("82.80"),
+ contact=cls.a8,
+ publisher=cls.p3,
+ pubdate=datetime.date(1995, 1, 15),
+ weight=4.5,
+ )
cls.b6 = HardbackBook.objects.create(
- isbn='155860191', name='Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp',
- pages=946, rating=5.0, price=Decimal('75.00'), contact=cls.a8, publisher=cls.p4,
- pubdate=datetime.date(1991, 10, 15), weight=3.7)
+ isbn="155860191",
+ name="Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp",
+ pages=946,
+ rating=5.0,
+ price=Decimal("75.00"),
+ contact=cls.a8,
+ publisher=cls.p4,
+ pubdate=datetime.date(1991, 10, 15),
+ weight=3.7,
+ )
cls.b1.authors.add(cls.a1, cls.a2)
cls.b2.authors.add(cls.a3)
cls.b3.authors.add(cls.a4)
@@ -84,19 +140,19 @@ class AggregationTests(TestCase):
cls.b6.authors.add(cls.a8)
s1 = Store.objects.create(
- name='Amazon.com',
+ name="Amazon.com",
original_opening=datetime.datetime(1994, 4, 23, 9, 17, 42),
- friday_night_closing=datetime.time(23, 59, 59)
+ friday_night_closing=datetime.time(23, 59, 59),
)
s2 = Store.objects.create(
- name='Books.com',
+ name="Books.com",
original_opening=datetime.datetime(2001, 3, 15, 11, 23, 37),
- friday_night_closing=datetime.time(23, 59, 59)
+ friday_night_closing=datetime.time(23, 59, 59),
)
s3 = Store.objects.create(
name="Mamma and Pappa's Books",
original_opening=datetime.datetime(1945, 4, 25, 16, 24, 14),
- friday_night_closing=datetime.time(21, 30)
+ friday_night_closing=datetime.time(21, 30),
)
s1.books.add(cls.b1, cls.b2, cls.b3, cls.b4, cls.b5, cls.b6)
s2.books.add(cls.b1, cls.b3, cls.b5, cls.b6)
@@ -107,16 +163,21 @@ class AggregationTests(TestCase):
self.assertEqual(getattr(obj, attr), value)
def test_annotation_with_value(self):
- values = Book.objects.filter(
- name='Practical Django Projects',
- ).annotate(
- discount_price=F('price') * 2,
- ).values(
- 'discount_price',
- ).annotate(sum_discount=Sum('discount_price'))
+ values = (
+ Book.objects.filter(
+ name="Practical Django Projects",
+ )
+ .annotate(
+ discount_price=F("price") * 2,
+ )
+ .values(
+ "discount_price",
+ )
+ .annotate(sum_discount=Sum("discount_price"))
+ )
self.assertSequenceEqual(
values,
- [{'discount_price': Decimal('59.38'), 'sum_discount': Decimal('59.38')}]
+ [{"discount_price": Decimal("59.38"), "sum_discount": Decimal("59.38")}],
)
def test_aggregates_in_where_clause(self):
@@ -130,11 +191,11 @@ class AggregationTests(TestCase):
Before the corresponding fix for this bug, this test passed in 1.1 and
failed in 1.2-beta (trunk).
"""
- qs = Book.objects.values('contact').annotate(Max('id'))
- qs = qs.order_by('contact').values_list('id__max', flat=True)
+ qs = Book.objects.values("contact").annotate(Max("id"))
+ qs = qs.order_by("contact").values_list("id__max", flat=True)
# don't do anything with the queryset (qs) before including it as a
# subquery
- books = Book.objects.order_by('id')
+ books = Book.objects.order_by("id")
qs1 = books.filter(id__in=qs)
qs2 = books.filter(id__in=list(qs))
self.assertEqual(list(qs1), list(qs2))
@@ -150,17 +211,17 @@ class AggregationTests(TestCase):
Before the corresponding fix for this bug, this test failed in both
1.1 and 1.2-beta (trunk).
"""
- qs = Book.objects.values('contact').annotate(Max('id'))
- qs = qs.order_by('contact').values_list('id__max', flat=True)
+ qs = Book.objects.values("contact").annotate(Max("id"))
+ qs = qs.order_by("contact").values_list("id__max", flat=True)
# force the queryset (qs) for the subquery to be evaluated in its
# current state
list(qs)
- books = Book.objects.order_by('id')
+ books = Book.objects.order_by("id")
qs1 = books.filter(id__in=qs)
qs2 = books.filter(id__in=list(qs))
self.assertEqual(list(qs1), list(qs2))
- @skipUnlessDBFeature('supports_subqueries_in_group_by')
+ @skipUnlessDBFeature("supports_subqueries_in_group_by")
def test_annotate_with_extra(self):
"""
Regression test for #11916: Extra params + aggregation creates
@@ -176,9 +237,11 @@ class AggregationTests(TestCase):
"""
# tests that this query does not raise a DatabaseError due to the full
# subselect being (erroneously) added to the GROUP BY parameters
- qs = Publisher.objects.extra(select={
- 'name_of_shortest_book': shortest_book_sql,
- }).annotate(total_books=Count('book'))
+ qs = Publisher.objects.extra(
+ select={
+ "name_of_shortest_book": shortest_book_sql,
+ }
+ ).annotate(total_books=Count("book"))
# force execution of the query
list(qs)
@@ -186,7 +249,7 @@ class AggregationTests(TestCase):
# Ordering requests are ignored
self.assertEqual(
Author.objects.order_by("name").aggregate(Avg("age")),
- {"age__avg": Approximate(37.444, places=1)}
+ {"age__avg": Approximate(37.444, places=1)},
)
# Implicit ordering is also ignored
@@ -197,153 +260,208 @@ class AggregationTests(TestCase):
# Baseline results
self.assertEqual(
- Book.objects.aggregate(Sum('pages'), Avg('pages')),
- {'pages__sum': 3703, 'pages__avg': Approximate(617.166, places=2)}
+ Book.objects.aggregate(Sum("pages"), Avg("pages")),
+ {"pages__sum": 3703, "pages__avg": Approximate(617.166, places=2)},
)
# Empty values query doesn't affect grouping or results
self.assertEqual(
- Book.objects.values().aggregate(Sum('pages'), Avg('pages')),
- {'pages__sum': 3703, 'pages__avg': Approximate(617.166, places=2)}
+ Book.objects.values().aggregate(Sum("pages"), Avg("pages")),
+ {"pages__sum": 3703, "pages__avg": Approximate(617.166, places=2)},
)
# Aggregate overrides extra selected column
self.assertEqual(
- Book.objects.extra(select={'price_per_page': 'price / pages'}).aggregate(Sum('pages')),
- {'pages__sum': 3703}
+ Book.objects.extra(select={"price_per_page": "price / pages"}).aggregate(
+ Sum("pages")
+ ),
+ {"pages__sum": 3703},
)
def test_annotation(self):
# Annotations get combined with extra select clauses
- obj = Book.objects.annotate(mean_auth_age=Avg("authors__age")).extra(
- select={"manufacture_cost": "price * .5"}).get(pk=self.b2.pk)
+ obj = (
+ Book.objects.annotate(mean_auth_age=Avg("authors__age"))
+ .extra(select={"manufacture_cost": "price * .5"})
+ .get(pk=self.b2.pk)
+ )
self.assertObjectAttrs(
obj,
contact_id=self.a3.id,
- isbn='067232959',
+ isbn="067232959",
mean_auth_age=45.0,
- name='Sams Teach Yourself Django in 24 Hours',
+ name="Sams Teach Yourself Django in 24 Hours",
pages=528,
price=Decimal("23.09"),
pubdate=datetime.date(2008, 3, 3),
publisher_id=self.p2.id,
- rating=3.0
+ rating=3.0,
)
# Different DB backends return different types for the extra select computation
- self.assertIn(obj.manufacture_cost, (11.545, Decimal('11.545')))
+ self.assertIn(obj.manufacture_cost, (11.545, Decimal("11.545")))
# Order of the annotate/extra in the query doesn't matter
- obj = Book.objects.extra(select={'manufacture_cost': 'price * .5'}).annotate(
- mean_auth_age=Avg('authors__age')).get(pk=self.b2.pk)
+ obj = (
+ Book.objects.extra(select={"manufacture_cost": "price * .5"})
+ .annotate(mean_auth_age=Avg("authors__age"))
+ .get(pk=self.b2.pk)
+ )
self.assertObjectAttrs(
obj,
contact_id=self.a3.id,
- isbn='067232959',
+ isbn="067232959",
mean_auth_age=45.0,
- name='Sams Teach Yourself Django in 24 Hours',
+ name="Sams Teach Yourself Django in 24 Hours",
pages=528,
price=Decimal("23.09"),
pubdate=datetime.date(2008, 3, 3),
publisher_id=self.p2.id,
- rating=3.0
+ rating=3.0,
)
# Different DB backends return different types for the extra select computation
- self.assertIn(obj.manufacture_cost, (11.545, Decimal('11.545')))
+ self.assertIn(obj.manufacture_cost, (11.545, Decimal("11.545")))
# Values queries can be combined with annotate and extra
- obj = Book.objects.annotate(mean_auth_age=Avg('authors__age')).extra(
- select={'manufacture_cost': 'price * .5'}).values().get(pk=self.b2.pk)
- manufacture_cost = obj['manufacture_cost']
- self.assertIn(manufacture_cost, (11.545, Decimal('11.545')))
- del obj['manufacture_cost']
- self.assertEqual(obj, {
- 'id': self.b2.id,
- 'contact_id': self.a3.id,
- 'isbn': '067232959',
- 'mean_auth_age': 45.0,
- 'name': 'Sams Teach Yourself Django in 24 Hours',
- 'pages': 528,
- 'price': Decimal('23.09'),
- 'pubdate': datetime.date(2008, 3, 3),
- 'publisher_id': self.p2.id,
- 'rating': 3.0,
- })
+ obj = (
+ Book.objects.annotate(mean_auth_age=Avg("authors__age"))
+ .extra(select={"manufacture_cost": "price * .5"})
+ .values()
+ .get(pk=self.b2.pk)
+ )
+ manufacture_cost = obj["manufacture_cost"]
+ self.assertIn(manufacture_cost, (11.545, Decimal("11.545")))
+ del obj["manufacture_cost"]
+ self.assertEqual(
+ obj,
+ {
+ "id": self.b2.id,
+ "contact_id": self.a3.id,
+ "isbn": "067232959",
+ "mean_auth_age": 45.0,
+ "name": "Sams Teach Yourself Django in 24 Hours",
+ "pages": 528,
+ "price": Decimal("23.09"),
+ "pubdate": datetime.date(2008, 3, 3),
+ "publisher_id": self.p2.id,
+ "rating": 3.0,
+ },
+ )
# The order of the (empty) values, annotate and extra clauses doesn't
# matter
- obj = Book.objects.values().annotate(mean_auth_age=Avg('authors__age')).extra(
- select={'manufacture_cost': 'price * .5'}).get(pk=self.b2.pk)
- manufacture_cost = obj['manufacture_cost']
- self.assertIn(manufacture_cost, (11.545, Decimal('11.545')))
- del obj['manufacture_cost']
- self.assertEqual(obj, {
- 'id': self.b2.id,
- 'contact_id': self.a3.id,
- 'isbn': '067232959',
- 'mean_auth_age': 45.0,
- 'name': 'Sams Teach Yourself Django in 24 Hours',
- 'pages': 528,
- 'price': Decimal('23.09'),
- 'pubdate': datetime.date(2008, 3, 3),
- 'publisher_id': self.p2.id,
- 'rating': 3.0
- })
+ obj = (
+ Book.objects.values()
+ .annotate(mean_auth_age=Avg("authors__age"))
+ .extra(select={"manufacture_cost": "price * .5"})
+ .get(pk=self.b2.pk)
+ )
+ manufacture_cost = obj["manufacture_cost"]
+ self.assertIn(manufacture_cost, (11.545, Decimal("11.545")))
+ del obj["manufacture_cost"]
+ self.assertEqual(
+ obj,
+ {
+ "id": self.b2.id,
+ "contact_id": self.a3.id,
+ "isbn": "067232959",
+ "mean_auth_age": 45.0,
+ "name": "Sams Teach Yourself Django in 24 Hours",
+ "pages": 528,
+ "price": Decimal("23.09"),
+ "pubdate": datetime.date(2008, 3, 3),
+ "publisher_id": self.p2.id,
+ "rating": 3.0,
+ },
+ )
# If the annotation precedes the values clause, it won't be included
# unless it is explicitly named
- obj = Book.objects.annotate(mean_auth_age=Avg('authors__age')).extra(
- select={'price_per_page': 'price / pages'}).values('name').get(pk=self.b1.pk)
- self.assertEqual(obj, {
- "name": 'The Definitive Guide to Django: Web Development Done Right',
- })
-
- obj = Book.objects.annotate(mean_auth_age=Avg('authors__age')).extra(
- select={'price_per_page': 'price / pages'}).values('name', 'mean_auth_age').get(pk=self.b1.pk)
- self.assertEqual(obj, {
- 'mean_auth_age': 34.5,
- 'name': 'The Definitive Guide to Django: Web Development Done Right',
- })
+ obj = (
+ Book.objects.annotate(mean_auth_age=Avg("authors__age"))
+ .extra(select={"price_per_page": "price / pages"})
+ .values("name")
+ .get(pk=self.b1.pk)
+ )
+ self.assertEqual(
+ obj,
+ {
+ "name": "The Definitive Guide to Django: Web Development Done Right",
+ },
+ )
+
+ obj = (
+ Book.objects.annotate(mean_auth_age=Avg("authors__age"))
+ .extra(select={"price_per_page": "price / pages"})
+ .values("name", "mean_auth_age")
+ .get(pk=self.b1.pk)
+ )
+ self.assertEqual(
+ obj,
+ {
+ "mean_auth_age": 34.5,
+ "name": "The Definitive Guide to Django: Web Development Done Right",
+ },
+ )
# If an annotation isn't included in the values, it can still be used
# in a filter
- qs = Book.objects.annotate(n_authors=Count('authors')).values('name').filter(n_authors__gt=2)
+ qs = (
+ Book.objects.annotate(n_authors=Count("authors"))
+ .values("name")
+ .filter(n_authors__gt=2)
+ )
self.assertSequenceEqual(
- qs, [
- {"name": 'Python Web Development with Django'}
- ],
+ qs,
+ [{"name": "Python Web Development with Django"}],
)
# The annotations are added to values output if values() precedes
# annotate()
- obj = Book.objects.values('name').annotate(mean_auth_age=Avg('authors__age')).extra(
- select={'price_per_page': 'price / pages'}).get(pk=self.b1.pk)
- self.assertEqual(obj, {
- 'mean_auth_age': 34.5,
- 'name': 'The Definitive Guide to Django: Web Development Done Right',
- })
+ obj = (
+ Book.objects.values("name")
+ .annotate(mean_auth_age=Avg("authors__age"))
+ .extra(select={"price_per_page": "price / pages"})
+ .get(pk=self.b1.pk)
+ )
+ self.assertEqual(
+ obj,
+ {
+ "mean_auth_age": 34.5,
+ "name": "The Definitive Guide to Django: Web Development Done Right",
+ },
+ )
# All of the objects are getting counted (allow_nulls) and that values
# respects the amount of objects
- self.assertEqual(
- len(Author.objects.annotate(Avg('friends__age')).values()),
- 9
- )
+ self.assertEqual(len(Author.objects.annotate(Avg("friends__age")).values()), 9)
# Consecutive calls to annotate accumulate in the query
qs = (
- Book.objects
- .values('price')
- .annotate(oldest=Max('authors__age'))
- .order_by('oldest', 'price')
- .annotate(Max('publisher__num_awards'))
+ Book.objects.values("price")
+ .annotate(oldest=Max("authors__age"))
+ .order_by("oldest", "price")
+ .annotate(Max("publisher__num_awards"))
)
self.assertSequenceEqual(
- qs, [
- {'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}
+ qs,
+ [
+ {"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,
+ },
],
)
@@ -351,68 +469,90 @@ class AggregationTests(TestCase):
# Aggregates can be composed over annotations.
# The return type is derived from the composed aggregate
vals = (
- Book.objects
- .all()
- .annotate(num_authors=Count('authors__id'))
- .aggregate(Max('pages'), Max('price'), Sum('num_authors'), Avg('num_authors'))
- )
- self.assertEqual(vals, {
- 'num_authors__sum': 10,
- 'num_authors__avg': Approximate(1.666, places=2),
- 'pages__max': 1132,
- 'price__max': Decimal("82.80")
- })
+ Book.objects.all()
+ .annotate(num_authors=Count("authors__id"))
+ .aggregate(
+ Max("pages"), Max("price"), Sum("num_authors"), Avg("num_authors")
+ )
+ )
+ self.assertEqual(
+ vals,
+ {
+ "num_authors__sum": 10,
+ "num_authors__avg": Approximate(1.666, places=2),
+ "pages__max": 1132,
+ "price__max": Decimal("82.80"),
+ },
+ )
# Regression for #15624 - Missing SELECT columns when using values, annotate
# and aggregate in a single query
self.assertEqual(
- Book.objects.annotate(c=Count('authors')).values('c').aggregate(Max('c')),
- {'c__max': 3}
+ Book.objects.annotate(c=Count("authors")).values("c").aggregate(Max("c")),
+ {"c__max": 3},
)
def test_conditional_aggregate(self):
# Conditional aggregation of a grouped queryset.
self.assertEqual(
- Book.objects.annotate(c=Count('authors')).values('pk').aggregate(test=Sum(
- Case(When(c__gt=1, then=1))
- ))['test'],
- 3
+ Book.objects.annotate(c=Count("authors"))
+ .values("pk")
+ .aggregate(test=Sum(Case(When(c__gt=1, then=1))))["test"],
+ 3,
)
def test_sliced_conditional_aggregate(self):
self.assertEqual(
- Author.objects.all()[:5].aggregate(test=Sum(Case(
- When(age__lte=35, then=1)
- )))['test'],
- 3
+ Author.objects.all()[:5].aggregate(
+ test=Sum(Case(When(age__lte=35, then=1)))
+ )["test"],
+ 3,
)
def test_annotated_conditional_aggregate(self):
- annotated_qs = Book.objects.annotate(discount_price=F('price') * Decimal('0.75'))
+ annotated_qs = Book.objects.annotate(
+ discount_price=F("price") * Decimal("0.75")
+ )
self.assertAlmostEqual(
- annotated_qs.aggregate(test=Avg(Case(
- When(pages__lt=400, then='discount_price'),
- output_field=DecimalField()
- )))['test'],
- Decimal('22.27'), places=2
+ annotated_qs.aggregate(
+ test=Avg(
+ Case(
+ When(pages__lt=400, then="discount_price"),
+ output_field=DecimalField(),
+ )
+ )
+ )["test"],
+ Decimal("22.27"),
+ places=2,
)
def test_distinct_conditional_aggregate(self):
self.assertEqual(
- Book.objects.distinct().aggregate(test=Avg(Case(
- When(price=Decimal('29.69'), then='pages'),
- output_field=IntegerField()
- )))['test'],
- 325
+ Book.objects.distinct().aggregate(
+ test=Avg(
+ Case(
+ When(price=Decimal("29.69"), then="pages"),
+ output_field=IntegerField(),
+ )
+ )
+ )["test"],
+ 325,
)
def test_conditional_aggregate_on_complex_condition(self):
self.assertEqual(
- Book.objects.distinct().aggregate(test=Avg(Case(
- When(Q(price__gte=Decimal('29')) & Q(price__lt=Decimal('30')), then='pages'),
- output_field=IntegerField()
- )))['test'],
- 325
+ Book.objects.distinct().aggregate(
+ test=Avg(
+ Case(
+ When(
+ Q(price__gte=Decimal("29")) & Q(price__lt=Decimal("30")),
+ then="pages",
+ ),
+ output_field=IntegerField(),
+ )
+ )
+ )["test"],
+ 325,
)
def test_decimal_aggregate_annotation_filter(self):
@@ -421,12 +561,20 @@ class AggregationTests(TestCase):
Requires special handling on SQLite (#18247).
"""
self.assertEqual(
- len(Author.objects.annotate(sum=Sum('book_contact_set__price')).filter(sum__gt=Decimal(40))),
- 1
+ len(
+ Author.objects.annotate(sum=Sum("book_contact_set__price")).filter(
+ sum__gt=Decimal(40)
+ )
+ ),
+ 1,
)
self.assertEqual(
- len(Author.objects.annotate(sum=Sum('book_contact_set__price')).filter(sum__lte=Decimal(40))),
- 4
+ len(
+ Author.objects.annotate(sum=Sum("book_contact_set__price")).filter(
+ sum__lte=Decimal(40)
+ )
+ ),
+ 4,
)
def test_field_error(self):
@@ -437,10 +585,10 @@ class AggregationTests(TestCase):
"pubdate, publisher, publisher_id, rating, store, tags"
)
with self.assertRaisesMessage(FieldError, msg):
- Book.objects.all().aggregate(num_authors=Count('foo'))
+ Book.objects.all().aggregate(num_authors=Count("foo"))
with self.assertRaisesMessage(FieldError, msg):
- Book.objects.all().annotate(num_authors=Count('foo'))
+ Book.objects.all().annotate(num_authors=Count("foo"))
msg = (
"Cannot resolve keyword 'foo' into field. Choices are: authors, "
@@ -448,167 +596,169 @@ class AggregationTests(TestCase):
"pages, price, pubdate, publisher, publisher_id, rating, store, tags"
)
with self.assertRaisesMessage(FieldError, msg):
- Book.objects.all().annotate(num_authors=Count('authors__id')).aggregate(Max('foo'))
+ Book.objects.all().annotate(num_authors=Count("authors__id")).aggregate(
+ Max("foo")
+ )
def test_more(self):
# Old-style count aggregations can be mixed with new-style
- self.assertEqual(
- Book.objects.annotate(num_authors=Count('authors')).count(),
- 6
- )
+ self.assertEqual(Book.objects.annotate(num_authors=Count("authors")).count(), 6)
# Non-ordinal, non-computed Aggregates over annotations correctly
# inherit the annotation's internal type if the annotation is ordinal
# or computed
- vals = Book.objects.annotate(num_authors=Count('authors')).aggregate(Max('num_authors'))
- self.assertEqual(
- vals,
- {'num_authors__max': 3}
+ vals = Book.objects.annotate(num_authors=Count("authors")).aggregate(
+ Max("num_authors")
)
+ self.assertEqual(vals, {"num_authors__max": 3})
- vals = Publisher.objects.annotate(avg_price=Avg('book__price')).aggregate(Max('avg_price'))
- self.assertEqual(
- vals,
- {'avg_price__max': 75.0}
+ vals = Publisher.objects.annotate(avg_price=Avg("book__price")).aggregate(
+ Max("avg_price")
)
+ self.assertEqual(vals, {"avg_price__max": 75.0})
# Aliases are quoted to protected aliases that might be reserved names
- vals = Book.objects.aggregate(number=Max('pages'), select=Max('pages'))
- self.assertEqual(
- vals,
- {'number': 1132, 'select': 1132}
- )
+ vals = Book.objects.aggregate(number=Max("pages"), select=Max("pages"))
+ self.assertEqual(vals, {"number": 1132, "select": 1132})
# Regression for #10064: select_related() plays nice with aggregates
- obj = Book.objects.select_related('publisher').annotate(
- num_authors=Count('authors')).values().get(isbn='013790395')
- self.assertEqual(obj, {
- 'contact_id': self.a8.id,
- 'id': self.b5.id,
- 'isbn': '013790395',
- 'name': 'Artificial Intelligence: A Modern Approach',
- 'num_authors': 2,
- 'pages': 1132,
- 'price': Decimal("82.8"),
- 'pubdate': datetime.date(1995, 1, 15),
- 'publisher_id': self.p3.id,
- 'rating': 4.0,
- })
+ obj = (
+ Book.objects.select_related("publisher")
+ .annotate(num_authors=Count("authors"))
+ .values()
+ .get(isbn="013790395")
+ )
+ self.assertEqual(
+ obj,
+ {
+ "contact_id": self.a8.id,
+ "id": self.b5.id,
+ "isbn": "013790395",
+ "name": "Artificial Intelligence: A Modern Approach",
+ "num_authors": 2,
+ "pages": 1132,
+ "price": Decimal("82.8"),
+ "pubdate": datetime.date(1995, 1, 15),
+ "publisher_id": self.p3.id,
+ "rating": 4.0,
+ },
+ )
# Regression for #10010: exclude on an aggregate field is correctly
# negated
+ self.assertEqual(len(Book.objects.annotate(num_authors=Count("authors"))), 6)
self.assertEqual(
- len(Book.objects.annotate(num_authors=Count('authors'))),
- 6
- )
- self.assertEqual(
- len(Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=2)),
- 1
+ len(
+ Book.objects.annotate(num_authors=Count("authors")).filter(
+ num_authors__gt=2
+ )
+ ),
+ 1,
)
self.assertEqual(
- len(Book.objects.annotate(num_authors=Count('authors')).exclude(num_authors__gt=2)),
- 5
+ len(
+ Book.objects.annotate(num_authors=Count("authors")).exclude(
+ num_authors__gt=2
+ )
+ ),
+ 5,
)
self.assertEqual(
len(
- Book.objects
- .annotate(num_authors=Count('authors'))
+ Book.objects.annotate(num_authors=Count("authors"))
.filter(num_authors__lt=3)
.exclude(num_authors__lt=2)
),
- 2
+ 2,
)
self.assertEqual(
len(
- Book.objects
- .annotate(num_authors=Count('authors'))
+ Book.objects.annotate(num_authors=Count("authors"))
.exclude(num_authors__lt=2)
.filter(num_authors__lt=3)
),
- 2
+ 2,
)
def test_aggregate_fexpr(self):
# Aggregates can be used with F() expressions
# ... where the F() is pushed into the HAVING clause
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')
+ Publisher.objects.annotate(num_books=Count("book"))
+ .filter(num_books__lt=F("num_awards") / 2)
+ .order_by("name")
+ .values("name", "num_books", "num_awards")
)
self.assertSequenceEqual(
- qs, [
- {'num_books': 1, 'name': 'Morgan Kaufmann', 'num_awards': 9},
- {'num_books': 2, 'name': 'Prentice Hall', 'num_awards': 7}
+ qs,
+ [
+ {"num_books": 1, "name": "Morgan Kaufmann", "num_awards": 9},
+ {"num_books": 2, "name": "Prentice Hall", "num_awards": 7},
],
)
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')
+ Publisher.objects.annotate(num_books=Count("book"))
+ .exclude(num_books__lt=F("num_awards") / 2)
+ .order_by("name")
+ .values("name", "num_books", "num_awards")
)
self.assertSequenceEqual(
- qs, [
- {'num_books': 2, 'name': 'Apress', 'num_awards': 3},
- {'num_books': 0, 'name': "Jonno's House of Books", 'num_awards': 0},
- {'num_books': 1, 'name': 'Sams', 'num_awards': 1}
+ qs,
+ [
+ {"num_books": 2, "name": "Apress", "num_awards": 3},
+ {"num_books": 0, "name": "Jonno's House of Books", "num_awards": 0},
+ {"num_books": 1, "name": "Sams", "num_awards": 1},
],
)
# ... and where the F() references an aggregate
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')
+ Publisher.objects.annotate(num_books=Count("book"))
+ .filter(num_awards__gt=2 * F("num_books"))
+ .order_by("name")
+ .values("name", "num_books", "num_awards")
)
self.assertSequenceEqual(
- qs, [
- {'num_books': 1, 'name': 'Morgan Kaufmann', 'num_awards': 9},
- {'num_books': 2, 'name': 'Prentice Hall', 'num_awards': 7}
+ qs,
+ [
+ {"num_books": 1, "name": "Morgan Kaufmann", "num_awards": 9},
+ {"num_books": 2, "name": "Prentice Hall", "num_awards": 7},
],
)
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')
+ Publisher.objects.annotate(num_books=Count("book"))
+ .exclude(num_books__lt=F("num_awards") / 2)
+ .order_by("name")
+ .values("name", "num_books", "num_awards")
)
self.assertSequenceEqual(
- qs, [
- {'num_books': 2, 'name': 'Apress', 'num_awards': 3},
- {'num_books': 0, 'name': "Jonno's House of Books", 'num_awards': 0},
- {'num_books': 1, 'name': 'Sams', 'num_awards': 1}
+ qs,
+ [
+ {"num_books": 2, "name": "Apress", "num_awards": 3},
+ {"num_books": 0, "name": "Jonno's House of Books", "num_awards": 0},
+ {"num_books": 1, "name": "Sams", "num_awards": 1},
],
)
def test_db_col_table(self):
# Tests on fields with non-default table and column names.
- qs = (
- Clues.objects
- .values('EntryID__Entry')
- .annotate(Appearances=Count('EntryID'), Distinct_Clues=Count('Clue', distinct=True))
+ qs = Clues.objects.values("EntryID__Entry").annotate(
+ Appearances=Count("EntryID"), Distinct_Clues=Count("Clue", distinct=True)
)
self.assertQuerysetEqual(qs, [])
- qs = Entries.objects.annotate(clue_count=Count('clues__ID'))
+ qs = Entries.objects.annotate(clue_count=Count("clues__ID"))
self.assertQuerysetEqual(qs, [])
def test_boolean_conversion(self):
# Aggregates mixed up ordering of columns for backend's convert_values
# method. Refs #21126.
- e = Entries.objects.create(Entry='foo')
- c = Clues.objects.create(EntryID=e, Clue='bar')
- qs = Clues.objects.select_related('EntryID').annotate(Count('ID'))
+ e = Entries.objects.create(Entry="foo")
+ c = Clues.objects.create(EntryID=e, Clue="bar")
+ qs = Clues.objects.select_related("EntryID").annotate(Count("ID"))
self.assertSequenceEqual(qs, [c])
self.assertEqual(qs[0].EntryID, e)
self.assertIs(qs[0].EntryID.Exclude, False)
@@ -616,50 +766,51 @@ class AggregationTests(TestCase):
def test_empty(self):
# Regression for #10089: Check handling of empty result sets with
# aggregates
- self.assertEqual(
- Book.objects.filter(id__in=[]).count(),
- 0
- )
+ self.assertEqual(Book.objects.filter(id__in=[]).count(), 0)
- 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'),
- )
+ 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"),
)
self.assertEqual(
vals,
- {'max_authors': None, 'max_rating': None, 'num_authors': 0, 'avg_authors': None, 'max_price': None}
+ {
+ "max_authors": None,
+ "max_rating": None,
+ "num_authors": 0,
+ "avg_authors": None,
+ "max_price": None,
+ },
)
qs = (
- Publisher.objects
- .filter(name="Jonno's House of Books")
+ Publisher.objects.filter(name="Jonno's House of Books")
.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()
+ 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()
)
self.assertSequenceEqual(
qs,
- [{
- 'max_authors': None,
- 'name': "Jonno's House of Books",
- 'num_awards': 0,
- 'max_price': None,
- 'num_authors': 0,
- 'max_rating': None,
- 'id': self.p5.id,
- 'avg_authors': None,
- }],
+ [
+ {
+ "max_authors": None,
+ "name": "Jonno's House of Books",
+ "num_awards": 0,
+ "max_price": None,
+ "num_authors": 0,
+ "max_rating": None,
+ "id": self.p5.id,
+ "avg_authors": None,
+ }
+ ],
)
def test_more_more(self):
@@ -667,7 +818,10 @@ class AggregationTests(TestCase):
# included in the GROUP BY. This only becomes a problem when the
# order_by introduces a new join.
self.assertQuerysetEqual(
- Book.objects.annotate(num_authors=Count('authors')).order_by('publisher__name', 'name'), [
+ Book.objects.annotate(num_authors=Count("authors")).order_by(
+ "publisher__name", "name"
+ ),
+ [
"Practical Django Projects",
"The Definitive Guide to Django: Web Development Done Right",
"Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp",
@@ -675,76 +829,97 @@ class AggregationTests(TestCase):
"Python Web Development with Django",
"Sams Teach Yourself Django in 24 Hours",
],
- lambda b: b.name
+ lambda b: b.name,
)
# Regression for #10127 - Empty select_related() works with annotate
- qs = Book.objects.filter(rating__lt=4.5).select_related().annotate(Avg('authors__age')).order_by('name')
+ qs = (
+ Book.objects.filter(rating__lt=4.5)
+ .select_related()
+ .annotate(Avg("authors__age"))
+ .order_by("name")
+ )
self.assertQuerysetEqual(
qs,
[
- ('Artificial Intelligence: A Modern Approach', 51.5, 'Prentice Hall', 'Peter Norvig'),
- ('Practical Django Projects', 29.0, 'Apress', 'James Bennett'),
(
- 'Python Web Development with Django',
+ "Artificial Intelligence: A Modern Approach",
+ 51.5,
+ "Prentice Hall",
+ "Peter Norvig",
+ ),
+ ("Practical Django Projects", 29.0, "Apress", "James Bennett"),
+ (
+ "Python Web Development with Django",
Approximate(30.333, places=2),
- 'Prentice Hall',
- 'Jeffrey Forcier',
+ "Prentice Hall",
+ "Jeffrey Forcier",
),
- ('Sams Teach Yourself Django in 24 Hours', 45.0, 'Sams', 'Brad Dayley')
+ ("Sams Teach Yourself Django in 24 Hours", 45.0, "Sams", "Brad Dayley"),
],
- lambda b: (b.name, b.authors__age__avg, b.publisher.name, b.contact.name)
+ lambda b: (b.name, b.authors__age__avg, b.publisher.name, b.contact.name),
)
# Regression for #10132 - If the values() clause only mentioned extra
# (select=) columns, those columns are used for grouping
- qs = Book.objects.extra(select={'pub': 'publisher_id'}).values('pub').annotate(Count('id')).order_by('pub')
+ qs = (
+ Book.objects.extra(select={"pub": "publisher_id"})
+ .values("pub")
+ .annotate(Count("id"))
+ .order_by("pub")
+ )
self.assertSequenceEqual(
- qs, [
- {'pub': self.p1.id, 'id__count': 2},
- {'pub': self.p2.id, 'id__count': 1},
- {'pub': self.p3.id, 'id__count': 2},
- {'pub': self.p4.id, 'id__count': 1},
+ qs,
+ [
+ {"pub": self.p1.id, "id__count": 2},
+ {"pub": self.p2.id, "id__count": 1},
+ {"pub": self.p3.id, "id__count": 2},
+ {"pub": self.p4.id, "id__count": 1},
],
)
qs = (
- Book.objects
- .extra(select={'pub': 'publisher_id', 'foo': 'pages'})
- .values('pub')
- .annotate(Count('id'))
- .order_by('pub')
+ Book.objects.extra(select={"pub": "publisher_id", "foo": "pages"})
+ .values("pub")
+ .annotate(Count("id"))
+ .order_by("pub")
)
self.assertSequenceEqual(
- qs, [
- {'pub': self.p1.id, 'id__count': 2},
- {'pub': self.p2.id, 'id__count': 1},
- {'pub': self.p3.id, 'id__count': 2},
- {'pub': self.p4.id, 'id__count': 1}
+ qs,
+ [
+ {"pub": self.p1.id, "id__count": 2},
+ {"pub": self.p2.id, "id__count": 1},
+ {"pub": self.p3.id, "id__count": 2},
+ {"pub": self.p4.id, "id__count": 1},
],
)
# Regression for #10182 - Queries with aggregate calls are correctly
# realiased when used in a subquery
ids = (
- Book.objects
- .filter(pages__gt=100)
- .annotate(n_authors=Count('authors'))
+ Book.objects.filter(pages__gt=100)
+ .annotate(n_authors=Count("authors"))
.filter(n_authors__gt=2)
- .order_by('n_authors')
+ .order_by("n_authors")
)
self.assertQuerysetEqual(
- Book.objects.filter(id__in=ids), [
+ Book.objects.filter(id__in=ids),
+ [
"Python Web Development with Django",
],
- lambda b: b.name
+ lambda b: b.name,
)
# Regression for #15709 - Ensure each group_by field only exists once
# per query
- qstr = str(Book.objects.values('publisher').annotate(max_pages=Max('pages')).order_by().query)
+ qstr = str(
+ Book.objects.values("publisher")
+ .annotate(max_pages=Max("pages"))
+ .order_by()
+ .query
+ )
# There is just one GROUP BY clause (zero commas means at most one clause).
- self.assertEqual(qstr[qstr.index('GROUP BY'):].count(', '), 0)
+ self.assertEqual(qstr[qstr.index("GROUP BY") :].count(", "), 0)
def test_duplicate_alias(self):
# Regression for #11256 - duplicating a default alias raises ValueError.
@@ -753,60 +928,74 @@ class AggregationTests(TestCase):
"the default name for another annotation."
)
with self.assertRaisesMessage(ValueError, msg):
- Book.objects.all().annotate(Avg('authors__age'), authors__age__avg=Avg('authors__age'))
+ Book.objects.all().annotate(
+ Avg("authors__age"), authors__age__avg=Avg("authors__age")
+ )
def test_field_name_conflict(self):
# Regression for #11256 - providing an aggregate name
# that conflicts with a field name on the model raises ValueError
msg = "The annotation 'age' conflicts with a field on the model."
with self.assertRaisesMessage(ValueError, msg):
- Author.objects.annotate(age=Avg('friends__age'))
+ Author.objects.annotate(age=Avg("friends__age"))
def test_m2m_name_conflict(self):
# Regression for #11256 - providing an aggregate name
# that conflicts with an m2m name on the model raises ValueError
msg = "The annotation 'friends' conflicts with a field on the model."
with self.assertRaisesMessage(ValueError, msg):
- Author.objects.annotate(friends=Count('friends'))
+ Author.objects.annotate(friends=Count("friends"))
def test_fk_attname_conflict(self):
msg = "The annotation 'contact_id' conflicts with a field on the model."
with self.assertRaisesMessage(ValueError, msg):
- Book.objects.annotate(contact_id=F('publisher_id'))
+ Book.objects.annotate(contact_id=F("publisher_id"))
def test_values_queryset_non_conflict(self):
# Regression for #14707 -- If you're using a values query set, some potential conflicts are avoided.
# age is a field on Author, so it shouldn't be allowed as an aggregate.
# But age isn't included in values(), so it is.
- results = Author.objects.values('name').annotate(age=Count('book_contact_set')).order_by('name')
+ results = (
+ Author.objects.values("name")
+ .annotate(age=Count("book_contact_set"))
+ .order_by("name")
+ )
self.assertEqual(len(results), 9)
- self.assertEqual(results[0]['name'], 'Adrian Holovaty')
- self.assertEqual(results[0]['age'], 1)
+ self.assertEqual(results[0]["name"], "Adrian Holovaty")
+ self.assertEqual(results[0]["age"], 1)
# Same problem, but aggregating over m2m fields
- results = Author.objects.values('name').annotate(age=Avg('friends__age')).order_by('name')
+ results = (
+ Author.objects.values("name")
+ .annotate(age=Avg("friends__age"))
+ .order_by("name")
+ )
self.assertEqual(len(results), 9)
- self.assertEqual(results[0]['name'], 'Adrian Holovaty')
- self.assertEqual(results[0]['age'], 32.0)
+ self.assertEqual(results[0]["name"], "Adrian Holovaty")
+ self.assertEqual(results[0]["age"], 32.0)
# Same problem, but colliding with an m2m field
- results = Author.objects.values('name').annotate(friends=Count('friends')).order_by('name')
+ results = (
+ Author.objects.values("name")
+ .annotate(friends=Count("friends"))
+ .order_by("name")
+ )
self.assertEqual(len(results), 9)
- self.assertEqual(results[0]['name'], 'Adrian Holovaty')
- self.assertEqual(results[0]['friends'], 2)
+ self.assertEqual(results[0]["name"], "Adrian Holovaty")
+ self.assertEqual(results[0]["friends"], 2)
def test_reverse_relation_name_conflict(self):
# Regression for #11256 - providing an aggregate name
# that conflicts with a reverse-related name on the model raises ValueError
msg = "The annotation 'book_contact_set' conflicts with a field on the model."
with self.assertRaisesMessage(ValueError, msg):
- Author.objects.annotate(book_contact_set=Avg('friends__age'))
+ Author.objects.annotate(book_contact_set=Avg("friends__age"))
def test_pickle(self):
# Regression for #10197 -- Queries with aggregates can be pickled.
# First check that pickling is possible at all. No crash = success
- qs = Book.objects.annotate(num_authors=Count('authors'))
+ qs = Book.objects.annotate(num_authors=Count("authors"))
pickle.dumps(qs)
# Then check that the round trip works.
@@ -823,21 +1012,27 @@ class AggregationTests(TestCase):
books = Book.objects.all()
books.aggregate(Avg("authors__age"))
self.assertQuerysetEqual(
- books.all(), [
- 'Artificial Intelligence: A Modern Approach',
- 'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp',
- 'Practical Django Projects',
- 'Python Web Development with Django',
- 'Sams Teach Yourself Django in 24 Hours',
- 'The Definitive Guide to Django: Web Development Done Right'
+ books.all(),
+ [
+ "Artificial Intelligence: A Modern Approach",
+ "Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp",
+ "Practical Django Projects",
+ "Python Web Development with Django",
+ "Sams Teach Yourself Django in 24 Hours",
+ "The Definitive Guide to Django: Web Development Done Right",
],
- lambda b: b.name
+ lambda b: b.name,
)
# Regression for #10248 - Annotations work with dates()
- qs = Book.objects.annotate(num_authors=Count('authors')).filter(num_authors=2).dates('pubdate', 'day')
+ qs = (
+ Book.objects.annotate(num_authors=Count("authors"))
+ .filter(num_authors=2)
+ .dates("pubdate", "day")
+ )
self.assertSequenceEqual(
- qs, [
+ qs,
+ [
datetime.date(1995, 1, 15),
datetime.date(2007, 12, 6),
],
@@ -846,116 +1041,90 @@ class AggregationTests(TestCase):
# Regression for #10290 - extra selects with parameters can be used for
# grouping.
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')
+ Book.objects.annotate(mean_auth_age=Avg("authors__age"))
+ .extra(select={"sheets": "(pages + %s) / %s"}, select_params=[1, 2])
+ .order_by("sheets")
+ .values("sheets")
)
self.assertQuerysetEqual(
- qs, [
- 150,
- 175,
- 224,
- 264,
- 473,
- 566
- ],
- lambda b: int(b["sheets"])
+ qs, [150, 175, 224, 264, 473, 566], lambda b: int(b["sheets"])
)
# Regression for 10425 - annotations don't get in the way of a count()
# clause
self.assertEqual(
- Book.objects.values('publisher').annotate(Count('publisher')).count(),
- 4
+ Book.objects.values("publisher").annotate(Count("publisher")).count(), 4
)
self.assertEqual(
- Book.objects.annotate(Count('publisher')).values('publisher').count(),
- 6
+ Book.objects.annotate(Count("publisher")).values("publisher").count(), 6
)
# Note: intentionally no order_by(), that case needs tests, too.
publishers = Publisher.objects.filter(id__in=[self.p1.id, self.p2.id])
- self.assertEqual(
- sorted(p.name for p in publishers),
- [
- "Apress",
- "Sams"
- ]
- )
+ self.assertEqual(sorted(p.name for p in publishers), ["Apress", "Sams"])
publishers = publishers.annotate(n_books=Count("book"))
sorted_publishers = sorted(publishers, key=lambda x: x.name)
- self.assertEqual(
- sorted_publishers[0].n_books,
- 2
- )
- self.assertEqual(
- sorted_publishers[1].n_books,
- 1
- )
+ self.assertEqual(sorted_publishers[0].n_books, 2)
+ self.assertEqual(sorted_publishers[1].n_books, 1)
- self.assertEqual(
- sorted(p.name for p in publishers),
- [
- "Apress",
- "Sams"
- ]
- )
+ self.assertEqual(sorted(p.name for p in publishers), ["Apress", "Sams"])
books = Book.objects.filter(publisher__in=publishers)
self.assertQuerysetEqual(
- books, [
+ books,
+ [
"Practical Django Projects",
"Sams Teach Yourself Django in 24 Hours",
"The Definitive Guide to Django: Web Development Done Right",
],
- lambda b: b.name
- )
- self.assertEqual(
- sorted(p.name for p in publishers),
- [
- "Apress",
- "Sams"
- ]
+ lambda b: b.name,
)
+ self.assertEqual(sorted(p.name for p in publishers), ["Apress", "Sams"])
# Regression for 10666 - inherited fields work with annotations and
# aggregations
self.assertEqual(
- HardbackBook.objects.aggregate(n_pages=Sum('book_ptr__pages')),
- {'n_pages': 2078}
+ HardbackBook.objects.aggregate(n_pages=Sum("book_ptr__pages")),
+ {"n_pages": 2078},
)
self.assertEqual(
- HardbackBook.objects.aggregate(n_pages=Sum('pages')),
- {'n_pages': 2078},
+ HardbackBook.objects.aggregate(n_pages=Sum("pages")),
+ {"n_pages": 2078},
)
- qs = HardbackBook.objects.annotate(
- n_authors=Count('book_ptr__authors'),
- ).values('name', 'n_authors').order_by('name')
+ qs = (
+ HardbackBook.objects.annotate(
+ n_authors=Count("book_ptr__authors"),
+ )
+ .values("name", "n_authors")
+ .order_by("name")
+ )
self.assertSequenceEqual(
qs,
[
- {'n_authors': 2, 'name': 'Artificial Intelligence: A Modern Approach'},
+ {"n_authors": 2, "name": "Artificial Intelligence: A Modern Approach"},
{
- 'n_authors': 1,
- 'name': 'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp'
- }
+ "n_authors": 1,
+ "name": "Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp",
+ },
],
)
- qs = HardbackBook.objects.annotate(n_authors=Count('authors')).values('name', 'n_authors').order_by('name')
+ qs = (
+ HardbackBook.objects.annotate(n_authors=Count("authors"))
+ .values("name", "n_authors")
+ .order_by("name")
+ )
self.assertSequenceEqual(
qs,
[
- {'n_authors': 2, 'name': 'Artificial Intelligence: A Modern Approach'},
+ {"n_authors": 2, "name": "Artificial Intelligence: A Modern Approach"},
{
- 'n_authors': 1,
- 'name': 'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp'
- }
+ "n_authors": 1,
+ "name": "Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp",
+ },
],
)
@@ -963,65 +1132,72 @@ class AggregationTests(TestCase):
# fields in an aggregate() call.
msg = "Cannot compute Avg('mean_age'): 'mean_age' is an aggregate"
with self.assertRaisesMessage(FieldError, msg):
- Book.objects.annotate(mean_age=Avg('authors__age')).annotate(Avg('mean_age'))
+ Book.objects.annotate(mean_age=Avg("authors__age")).annotate(
+ Avg("mean_age")
+ )
def test_empty_filter_count(self):
self.assertEqual(
- Author.objects.filter(id__in=[]).annotate(Count("friends")).count(),
- 0
+ Author.objects.filter(id__in=[]).annotate(Count("friends")).count(), 0
)
def test_empty_filter_aggregate(self):
self.assertEqual(
- Author.objects.filter(id__in=[]).annotate(Count("friends")).aggregate(Count("pk")),
- {"pk__count": 0}
+ Author.objects.filter(id__in=[])
+ .annotate(Count("friends"))
+ .aggregate(Count("pk")),
+ {"pk__count": 0},
)
def test_none_call_before_aggregate(self):
# Regression for #11789
self.assertEqual(
- Author.objects.none().aggregate(Avg('age')),
- {'age__avg': None}
+ Author.objects.none().aggregate(Avg("age")), {"age__avg": None}
)
def test_annotate_and_join(self):
self.assertEqual(
- Author.objects.annotate(c=Count("friends__name")).exclude(friends__name="Joe").count(),
- Author.objects.count()
+ Author.objects.annotate(c=Count("friends__name"))
+ .exclude(friends__name="Joe")
+ .count(),
+ Author.objects.count(),
)
def test_f_expression_annotation(self):
# Books with less than 200 pages per author.
- qs = Book.objects.values("name").annotate(
- n_authors=Count("authors")
- ).filter(
- pages__lt=F("n_authors") * 200
- ).values_list("pk")
+ qs = (
+ Book.objects.values("name")
+ .annotate(n_authors=Count("authors"))
+ .filter(pages__lt=F("n_authors") * 200)
+ .values_list("pk")
+ )
self.assertQuerysetEqual(
- Book.objects.filter(pk__in=qs), [
- "Python Web Development with Django"
- ],
- attrgetter("name")
+ Book.objects.filter(pk__in=qs),
+ ["Python Web Development with Django"],
+ attrgetter("name"),
)
def test_values_annotate_values(self):
- qs = Book.objects.values("name").annotate(
- n_authors=Count("authors")
- ).values_list("pk", flat=True).order_by('name')
+ qs = (
+ Book.objects.values("name")
+ .annotate(n_authors=Count("authors"))
+ .values_list("pk", flat=True)
+ .order_by("name")
+ )
self.assertEqual(list(qs), list(Book.objects.values_list("pk", flat=True)))
def test_having_group_by(self):
# When a field occurs on the LHS of a HAVING clause that it
# appears correctly in the GROUP BY clause
- qs = Book.objects.values_list("name").annotate(
- n_authors=Count("authors")
- ).filter(
- pages__gt=F("n_authors")
- ).values_list("name", flat=True).order_by('name')
- # Results should be the same, all Books have more pages than authors
- self.assertEqual(
- list(qs), list(Book.objects.values_list("name", flat=True))
+ qs = (
+ Book.objects.values_list("name")
+ .annotate(n_authors=Count("authors"))
+ .filter(pages__gt=F("n_authors"))
+ .values_list("name", flat=True)
+ .order_by("name")
)
+ # Results should be the same, all Books have more pages than authors
+ self.assertEqual(list(qs), list(Book.objects.values_list("name", flat=True)))
def test_values_list_annotation_args_ordering(self):
"""
@@ -1029,144 +1205,157 @@ class AggregationTests(TestCase):
**kwargs comes after *args.
Regression test for #23659.
"""
- books = Book.objects.values_list("publisher__name").annotate(
- Count("id"), Avg("price"), Avg("authors__age"), avg_pgs=Avg("pages")
- ).order_by("-publisher__name")
- self.assertEqual(books[0], ('Sams', 1, Decimal('23.09'), 45.0, 528.0))
+ books = (
+ Book.objects.values_list("publisher__name")
+ .annotate(
+ Count("id"), Avg("price"), Avg("authors__age"), avg_pgs=Avg("pages")
+ )
+ .order_by("-publisher__name")
+ )
+ self.assertEqual(books[0], ("Sams", 1, Decimal("23.09"), 45.0, 528.0))
def test_annotation_disjunction(self):
- qs = Book.objects.annotate(n_authors=Count("authors")).filter(
- Q(n_authors=2) | Q(name="Python Web Development with Django")
- ).order_by('name')
+ qs = (
+ Book.objects.annotate(n_authors=Count("authors"))
+ .filter(Q(n_authors=2) | Q(name="Python Web Development with Django"))
+ .order_by("name")
+ )
self.assertQuerysetEqual(
- qs, [
+ qs,
+ [
"Artificial Intelligence: A Modern Approach",
"Python Web Development with Django",
"The Definitive Guide to Django: Web Development Done Right",
],
- attrgetter("name")
+ attrgetter("name"),
)
qs = (
- Book.objects
- .annotate(n_authors=Count("authors"))
- .filter(
- Q(name="The Definitive Guide to Django: Web Development Done Right") |
- (Q(name="Artificial Intelligence: A Modern Approach") & Q(n_authors=3))
+ Book.objects.annotate(n_authors=Count("authors")).filter(
+ Q(name="The Definitive Guide to Django: Web Development Done Right")
+ | (
+ Q(name="Artificial Intelligence: A Modern Approach")
+ & Q(n_authors=3)
+ )
)
- ).order_by('name')
+ ).order_by("name")
self.assertQuerysetEqual(
qs,
[
"The Definitive Guide to Django: Web Development Done Right",
],
- attrgetter("name")
+ attrgetter("name"),
)
- qs = Publisher.objects.annotate(
- rating_sum=Sum("book__rating"),
- book_count=Count("book")
- ).filter(
- Q(rating_sum__gt=5.5) | Q(rating_sum__isnull=True)
- ).order_by('pk')
+ qs = (
+ Publisher.objects.annotate(
+ rating_sum=Sum("book__rating"), book_count=Count("book")
+ )
+ .filter(Q(rating_sum__gt=5.5) | Q(rating_sum__isnull=True))
+ .order_by("pk")
+ )
self.assertQuerysetEqual(
- qs, [
+ qs,
+ [
"Apress",
"Prentice Hall",
"Jonno's House of Books",
],
- attrgetter("name")
+ attrgetter("name"),
)
- qs = Publisher.objects.annotate(
- rating_sum=Sum("book__rating"),
- book_count=Count("book")
- ).filter(
- Q(rating_sum__gt=F("book_count")) | Q(rating_sum=None)
- ).order_by("num_awards")
+ qs = (
+ Publisher.objects.annotate(
+ rating_sum=Sum("book__rating"), book_count=Count("book")
+ )
+ .filter(Q(rating_sum__gt=F("book_count")) | Q(rating_sum=None))
+ .order_by("num_awards")
+ )
self.assertQuerysetEqual(
- qs, [
+ qs,
+ [
"Jonno's House of Books",
"Sams",
"Apress",
"Prentice Hall",
- "Morgan Kaufmann"
+ "Morgan Kaufmann",
],
- attrgetter("name")
+ attrgetter("name"),
)
def test_quoting_aggregate_order_by(self):
- qs = Book.objects.filter(
- name="Python Web Development with Django"
- ).annotate(
- authorCount=Count("authors")
- ).order_by("authorCount")
+ qs = (
+ Book.objects.filter(name="Python Web Development with Django")
+ .annotate(authorCount=Count("authors"))
+ .order_by("authorCount")
+ )
self.assertQuerysetEqual(
- qs, [
+ qs,
+ [
("Python Web Development with Django", 3),
],
- lambda b: (b.name, b.authorCount)
+ lambda b: (b.name, b.authorCount),
)
def test_stddev(self):
self.assertEqual(
- Book.objects.aggregate(StdDev('pages')),
- {'pages__stddev': Approximate(311.46, 1)}
+ Book.objects.aggregate(StdDev("pages")),
+ {"pages__stddev": Approximate(311.46, 1)},
)
self.assertEqual(
- Book.objects.aggregate(StdDev('rating')),
- {'rating__stddev': Approximate(0.60, 1)}
+ Book.objects.aggregate(StdDev("rating")),
+ {"rating__stddev": Approximate(0.60, 1)},
)
self.assertEqual(
- Book.objects.aggregate(StdDev('price')),
- {'price__stddev': Approximate(Decimal('24.16'), 2)}
+ Book.objects.aggregate(StdDev("price")),
+ {"price__stddev": Approximate(Decimal("24.16"), 2)},
)
self.assertEqual(
- Book.objects.aggregate(StdDev('pages', sample=True)),
- {'pages__stddev': Approximate(341.19, 2)}
+ Book.objects.aggregate(StdDev("pages", sample=True)),
+ {"pages__stddev": Approximate(341.19, 2)},
)
self.assertEqual(
- Book.objects.aggregate(StdDev('rating', sample=True)),
- {'rating__stddev': Approximate(0.66, 2)}
+ Book.objects.aggregate(StdDev("rating", sample=True)),
+ {"rating__stddev": Approximate(0.66, 2)},
)
self.assertEqual(
- Book.objects.aggregate(StdDev('price', sample=True)),
- {'price__stddev': Approximate(Decimal('26.46'), 1)}
+ Book.objects.aggregate(StdDev("price", sample=True)),
+ {"price__stddev": Approximate(Decimal("26.46"), 1)},
)
self.assertEqual(
- Book.objects.aggregate(Variance('pages')),
- {'pages__variance': Approximate(97010.80, 1)}
+ Book.objects.aggregate(Variance("pages")),
+ {"pages__variance": Approximate(97010.80, 1)},
)
self.assertEqual(
- Book.objects.aggregate(Variance('rating')),
- {'rating__variance': Approximate(0.36, 1)}
+ Book.objects.aggregate(Variance("rating")),
+ {"rating__variance": Approximate(0.36, 1)},
)
self.assertEqual(
- Book.objects.aggregate(Variance('price')),
- {'price__variance': Approximate(Decimal('583.77'), 1)}
+ Book.objects.aggregate(Variance("price")),
+ {"price__variance": Approximate(Decimal("583.77"), 1)},
)
self.assertEqual(
- Book.objects.aggregate(Variance('pages', sample=True)),
- {'pages__variance': Approximate(116412.96, 1)}
+ Book.objects.aggregate(Variance("pages", sample=True)),
+ {"pages__variance": Approximate(116412.96, 1)},
)
self.assertEqual(
- Book.objects.aggregate(Variance('rating', sample=True)),
- {'rating__variance': Approximate(0.44, 2)}
+ Book.objects.aggregate(Variance("rating", sample=True)),
+ {"rating__variance": Approximate(0.44, 2)},
)
self.assertEqual(
- Book.objects.aggregate(Variance('price', sample=True)),
- {'price__variance': Approximate(Decimal('700.53'), 2)}
+ Book.objects.aggregate(Variance("price", sample=True)),
+ {"price__variance": Approximate(Decimal("700.53"), 2)},
)
def test_filtering_by_annotation_name(self):
@@ -1174,31 +1363,31 @@ class AggregationTests(TestCase):
# The name of the explicitly provided annotation name in this case
# poses no problem
- qs = Author.objects.annotate(book_cnt=Count('book')).filter(book_cnt=2).order_by('name')
- self.assertQuerysetEqual(
- qs,
- ['Peter Norvig'],
- lambda b: b.name
+ qs = (
+ Author.objects.annotate(book_cnt=Count("book"))
+ .filter(book_cnt=2)
+ .order_by("name")
)
+ self.assertQuerysetEqual(qs, ["Peter Norvig"], lambda b: b.name)
# Neither in this case
- qs = Author.objects.annotate(book_count=Count('book')).filter(book_count=2).order_by('name')
- self.assertQuerysetEqual(
- qs,
- ['Peter Norvig'],
- lambda b: b.name
+ qs = (
+ Author.objects.annotate(book_count=Count("book"))
+ .filter(book_count=2)
+ .order_by("name")
)
+ self.assertQuerysetEqual(qs, ["Peter Norvig"], lambda b: b.name)
# This case used to fail because the ORM couldn't resolve the
# automatically generated annotation name `book__count`
- qs = Author.objects.annotate(Count('book')).filter(book__count=2).order_by('name')
- self.assertQuerysetEqual(
- qs,
- ['Peter Norvig'],
- lambda b: b.name
+ qs = (
+ Author.objects.annotate(Count("book"))
+ .filter(book__count=2)
+ .order_by("name")
)
+ self.assertQuerysetEqual(qs, ["Peter Norvig"], lambda b: b.name)
# Referencing the auto-generated name in an aggregate() also works.
self.assertEqual(
- Author.objects.annotate(Count('book')).aggregate(Max('book__count')),
- {'book__count__max': 2}
+ Author.objects.annotate(Count("book")).aggregate(Max("book__count")),
+ {"book__count__max": 2},
)
def test_annotate_joins(self):
@@ -1207,152 +1396,180 @@ class AggregationTests(TestCase):
cause the query generation to fail if there is an exclude() for fk-field
in the query, too. Refs #19087.
"""
- qs = Book.objects.annotate(n=Count('pk'))
- self.assertIs(qs.query.alias_map['aggregation_regress_book'].join_type, None)
+ qs = Book.objects.annotate(n=Count("pk"))
+ self.assertIs(qs.query.alias_map["aggregation_regress_book"].join_type, None)
# The query executes without problems.
self.assertEqual(len(qs.exclude(publisher=-1)), 6)
- @skipUnlessAnyDBFeature('allows_group_by_pk', 'allows_group_by_selected_pks')
+ @skipUnlessAnyDBFeature("allows_group_by_pk", "allows_group_by_selected_pks")
def test_aggregate_duplicate_columns(self):
# Regression test for #17144
- results = Author.objects.annotate(num_contacts=Count('book_contact_set'))
+ results = Author.objects.annotate(num_contacts=Count("book_contact_set"))
# There should only be one GROUP BY clause, for the `id` column.
# `name` and `age` should not be grouped on.
- _, _, group_by = results.query.get_compiler(using='default').pre_sql_setup()
+ _, _, group_by = results.query.get_compiler(using="default").pre_sql_setup()
self.assertEqual(len(group_by), 1)
- self.assertIn('id', group_by[0][0])
- self.assertNotIn('name', group_by[0][0])
- self.assertNotIn('age', group_by[0][0])
+ self.assertIn("id", group_by[0][0])
+ self.assertNotIn("name", group_by[0][0])
+ self.assertNotIn("age", group_by[0][0])
self.assertEqual(
- [(a.name, a.num_contacts) for a in results.order_by('name')],
+ [(a.name, a.num_contacts) for a in results.order_by("name")],
[
- ('Adrian Holovaty', 1),
- ('Brad Dayley', 1),
- ('Jacob Kaplan-Moss', 0),
- ('James Bennett', 1),
- ('Jeffrey Forcier', 1),
- ('Paul Bissex', 0),
- ('Peter Norvig', 2),
- ('Stuart Russell', 0),
- ('Wesley J. Chun', 0),
- ]
- )
-
- @skipUnlessAnyDBFeature('allows_group_by_pk', 'allows_group_by_selected_pks')
+ ("Adrian Holovaty", 1),
+ ("Brad Dayley", 1),
+ ("Jacob Kaplan-Moss", 0),
+ ("James Bennett", 1),
+ ("Jeffrey Forcier", 1),
+ ("Paul Bissex", 0),
+ ("Peter Norvig", 2),
+ ("Stuart Russell", 0),
+ ("Wesley J. Chun", 0),
+ ],
+ )
+
+ @skipUnlessAnyDBFeature("allows_group_by_pk", "allows_group_by_selected_pks")
def test_aggregate_duplicate_columns_only(self):
# Works with only() too.
- results = Author.objects.only('id', 'name').annotate(num_contacts=Count('book_contact_set'))
- _, _, grouping = results.query.get_compiler(using='default').pre_sql_setup()
+ results = Author.objects.only("id", "name").annotate(
+ num_contacts=Count("book_contact_set")
+ )
+ _, _, grouping = results.query.get_compiler(using="default").pre_sql_setup()
self.assertEqual(len(grouping), 1)
- self.assertIn('id', grouping[0][0])
- self.assertNotIn('name', grouping[0][0])
- self.assertNotIn('age', grouping[0][0])
+ self.assertIn("id", grouping[0][0])
+ self.assertNotIn("name", grouping[0][0])
+ self.assertNotIn("age", grouping[0][0])
self.assertEqual(
- [(a.name, a.num_contacts) for a in results.order_by('name')],
+ [(a.name, a.num_contacts) for a in results.order_by("name")],
[
- ('Adrian Holovaty', 1),
- ('Brad Dayley', 1),
- ('Jacob Kaplan-Moss', 0),
- ('James Bennett', 1),
- ('Jeffrey Forcier', 1),
- ('Paul Bissex', 0),
- ('Peter Norvig', 2),
- ('Stuart Russell', 0),
- ('Wesley J. Chun', 0),
- ]
- )
-
- @skipUnlessAnyDBFeature('allows_group_by_pk', 'allows_group_by_selected_pks')
+ ("Adrian Holovaty", 1),
+ ("Brad Dayley", 1),
+ ("Jacob Kaplan-Moss", 0),
+ ("James Bennett", 1),
+ ("Jeffrey Forcier", 1),
+ ("Paul Bissex", 0),
+ ("Peter Norvig", 2),
+ ("Stuart Russell", 0),
+ ("Wesley J. Chun", 0),
+ ],
+ )
+
+ @skipUnlessAnyDBFeature("allows_group_by_pk", "allows_group_by_selected_pks")
def test_aggregate_duplicate_columns_select_related(self):
# And select_related()
- results = Book.objects.select_related('contact').annotate(
- num_authors=Count('authors'))
- _, _, grouping = results.query.get_compiler(using='default').pre_sql_setup()
+ results = Book.objects.select_related("contact").annotate(
+ num_authors=Count("authors")
+ )
+ _, _, grouping = results.query.get_compiler(using="default").pre_sql_setup()
# In the case of `group_by_selected_pks` we also group by contact.id because of the select_related.
- self.assertEqual(len(grouping), 1 if connection.features.allows_group_by_pk else 2)
- self.assertIn('id', grouping[0][0])
- self.assertNotIn('name', grouping[0][0])
- self.assertNotIn('contact', grouping[0][0])
self.assertEqual(
- [(b.name, b.num_authors) for b in results.order_by('name')],
+ len(grouping), 1 if connection.features.allows_group_by_pk else 2
+ )
+ self.assertIn("id", grouping[0][0])
+ self.assertNotIn("name", grouping[0][0])
+ self.assertNotIn("contact", grouping[0][0])
+ self.assertEqual(
+ [(b.name, b.num_authors) for b in results.order_by("name")],
[
- ('Artificial Intelligence: A Modern Approach', 2),
- ('Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp', 1),
- ('Practical Django Projects', 1),
- ('Python Web Development with Django', 3),
- ('Sams Teach Yourself Django in 24 Hours', 1),
- ('The Definitive Guide to Django: Web Development Done Right', 2)
- ]
+ ("Artificial Intelligence: A Modern Approach", 2),
+ (
+ "Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp",
+ 1,
+ ),
+ ("Practical Django Projects", 1),
+ ("Python Web Development with Django", 3),
+ ("Sams Teach Yourself Django in 24 Hours", 1),
+ ("The Definitive Guide to Django: Web Development Done Right", 2),
+ ],
)
- @skipUnlessDBFeature('allows_group_by_selected_pks')
+ @skipUnlessDBFeature("allows_group_by_selected_pks")
def test_aggregate_unmanaged_model_columns(self):
"""
Unmanaged models are sometimes used to represent database views which
may not allow grouping by selected primary key.
"""
+
def assertQuerysetResults(queryset):
self.assertEqual(
- [(b.name, b.num_authors) for b in queryset.order_by('name')],
+ [(b.name, b.num_authors) for b in queryset.order_by("name")],
[
- ('Artificial Intelligence: A Modern Approach', 2),
- ('Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp', 1),
- ('Practical Django Projects', 1),
- ('Python Web Development with Django', 3),
- ('Sams Teach Yourself Django in 24 Hours', 1),
- ('The Definitive Guide to Django: Web Development Done Right', 2),
- ]
+ ("Artificial Intelligence: A Modern Approach", 2),
+ (
+ "Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp",
+ 1,
+ ),
+ ("Practical Django Projects", 1),
+ ("Python Web Development with Django", 3),
+ ("Sams Teach Yourself Django in 24 Hours", 1),
+ ("The Definitive Guide to Django: Web Development Done Right", 2),
+ ],
)
- queryset = Book.objects.select_related('contact').annotate(num_authors=Count('authors'))
+
+ queryset = Book.objects.select_related("contact").annotate(
+ num_authors=Count("authors")
+ )
# Unmanaged origin model.
- with mock.patch.object(Book._meta, 'managed', False):
- _, _, grouping = queryset.query.get_compiler(using='default').pre_sql_setup()
+ with mock.patch.object(Book._meta, "managed", False):
+ _, _, grouping = queryset.query.get_compiler(
+ using="default"
+ ).pre_sql_setup()
self.assertEqual(len(grouping), len(Book._meta.fields) + 1)
for index, field in enumerate(Book._meta.fields):
self.assertIn(field.name, grouping[index][0])
self.assertIn(Author._meta.pk.name, grouping[-1][0])
assertQuerysetResults(queryset)
# Unmanaged related model.
- with mock.patch.object(Author._meta, 'managed', False):
- _, _, grouping = queryset.query.get_compiler(using='default').pre_sql_setup()
+ with mock.patch.object(Author._meta, "managed", False):
+ _, _, grouping = queryset.query.get_compiler(
+ using="default"
+ ).pre_sql_setup()
self.assertEqual(len(grouping), len(Author._meta.fields) + 1)
self.assertIn(Book._meta.pk.name, grouping[0][0])
for index, field in enumerate(Author._meta.fields):
self.assertIn(field.name, grouping[index + 1][0])
assertQuerysetResults(queryset)
- @skipUnlessDBFeature('allows_group_by_selected_pks')
+ @skipUnlessDBFeature("allows_group_by_selected_pks")
def test_aggregate_unmanaged_model_as_tables(self):
- qs = Book.objects.select_related('contact').annotate(num_authors=Count('authors'))
+ qs = Book.objects.select_related("contact").annotate(
+ num_authors=Count("authors")
+ )
# Force treating unmanaged models as tables.
with mock.patch(
- 'django.db.connection.features.allows_group_by_selected_pks_on_model',
+ "django.db.connection.features.allows_group_by_selected_pks_on_model",
return_value=True,
):
- with mock.patch.object(Book._meta, 'managed', False), \
- mock.patch.object(Author._meta, 'managed', False):
- _, _, grouping = qs.query.get_compiler(using='default').pre_sql_setup()
+ with mock.patch.object(Book._meta, "managed", False), mock.patch.object(
+ Author._meta, "managed", False
+ ):
+ _, _, grouping = qs.query.get_compiler(using="default").pre_sql_setup()
self.assertEqual(len(grouping), 2)
- self.assertIn('id', grouping[0][0])
- self.assertIn('id', grouping[1][0])
+ self.assertIn("id", grouping[0][0])
+ self.assertIn("id", grouping[1][0])
self.assertQuerysetEqual(
- qs.order_by('name'),
+ qs.order_by("name"),
[
- ('Artificial Intelligence: A Modern Approach', 2),
- ('Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp', 1),
- ('Practical Django Projects', 1),
- ('Python Web Development with Django', 3),
- ('Sams Teach Yourself Django in 24 Hours', 1),
- ('The Definitive Guide to Django: Web Development Done Right', 2),
+ ("Artificial Intelligence: A Modern Approach", 2),
+ (
+ "Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp",
+ 1,
+ ),
+ ("Practical Django Projects", 1),
+ ("Python Web Development with Django", 3),
+ ("Sams Teach Yourself Django in 24 Hours", 1),
+ (
+ "The Definitive Guide to Django: Web Development Done Right",
+ 2,
+ ),
],
- attrgetter('name', 'num_authors'),
+ attrgetter("name", "num_authors"),
)
def test_reverse_join_trimming(self):
- qs = Author.objects.annotate(Count('book_contact_set__contact'))
- self.assertIn(' JOIN ', str(qs.query))
+ qs = Author.objects.annotate(Count("book_contact_set__contact"))
+ self.assertIn(" JOIN ", str(qs.query))
def test_aggregation_with_generic_reverse_relation(self):
"""
@@ -1361,13 +1578,15 @@ class AggregationTests(TestCase):
tests aggregations with generic reverse relations
"""
- django_book = Book.objects.get(name='Practical Django Projects')
+ django_book = Book.objects.get(name="Practical Django Projects")
ItemTag.objects.create(
- object_id=django_book.id, tag='intermediate',
+ object_id=django_book.id,
+ tag="intermediate",
content_type=ContentType.objects.get_for_model(django_book),
)
ItemTag.objects.create(
- object_id=django_book.id, tag='django',
+ object_id=django_book.id,
+ tag="django",
content_type=ContentType.objects.get_for_model(django_book),
)
# Assign a tag to model with same PK as the book above. If the JOIN
@@ -1375,84 +1594,89 @@ class AggregationTests(TestCase):
# condition the annotation will also count the 'hi mom' tag for b.
wmpk = WithManualPK.objects.create(id=django_book.pk)
ItemTag.objects.create(
- object_id=wmpk.id, tag='hi mom',
+ object_id=wmpk.id,
+ tag="hi mom",
content_type=ContentType.objects.get_for_model(wmpk),
)
- ai_book = Book.objects.get(name__startswith='Paradigms of Artificial Intelligence')
+ ai_book = Book.objects.get(
+ name__startswith="Paradigms of Artificial Intelligence"
+ )
ItemTag.objects.create(
- object_id=ai_book.id, tag='intermediate',
+ object_id=ai_book.id,
+ tag="intermediate",
content_type=ContentType.objects.get_for_model(ai_book),
)
- self.assertEqual(Book.objects.aggregate(Count('tags')), {'tags__count': 3})
- results = Book.objects.annotate(Count('tags')).order_by('-tags__count', 'name')
+ self.assertEqual(Book.objects.aggregate(Count("tags")), {"tags__count": 3})
+ results = Book.objects.annotate(Count("tags")).order_by("-tags__count", "name")
self.assertEqual(
[(b.name, b.tags__count) for b in results],
[
- ('Practical Django Projects', 2),
- ('Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp', 1),
- ('Artificial Intelligence: A Modern Approach', 0),
- ('Python Web Development with Django', 0),
- ('Sams Teach Yourself Django in 24 Hours', 0),
- ('The Definitive Guide to Django: Web Development Done Right', 0)
- ]
+ ("Practical Django Projects", 2),
+ (
+ "Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp",
+ 1,
+ ),
+ ("Artificial Intelligence: A Modern Approach", 0),
+ ("Python Web Development with Django", 0),
+ ("Sams Teach Yourself Django in 24 Hours", 0),
+ ("The Definitive Guide to Django: Web Development Done Right", 0),
+ ],
)
def test_negated_aggregation(self):
expected_results = Author.objects.exclude(
- pk__in=Author.objects.annotate(book_cnt=Count('book')).filter(book_cnt=2)
- ).order_by('name')
+ pk__in=Author.objects.annotate(book_cnt=Count("book")).filter(book_cnt=2)
+ ).order_by("name")
expected_results = [a.name for a in expected_results]
- qs = Author.objects.annotate(book_cnt=Count('book')).exclude(
- Q(book_cnt=2), Q(book_cnt=2)).order_by('name')
- self.assertQuerysetEqual(
- qs,
- expected_results,
- lambda b: b.name
+ qs = (
+ Author.objects.annotate(book_cnt=Count("book"))
+ .exclude(Q(book_cnt=2), Q(book_cnt=2))
+ .order_by("name")
)
+ self.assertQuerysetEqual(qs, expected_results, lambda b: b.name)
expected_results = Author.objects.exclude(
- pk__in=Author.objects.annotate(book_cnt=Count('book')).filter(book_cnt=2)
- ).order_by('name')
+ pk__in=Author.objects.annotate(book_cnt=Count("book")).filter(book_cnt=2)
+ ).order_by("name")
expected_results = [a.name for a in expected_results]
- qs = Author.objects.annotate(book_cnt=Count('book')).exclude(Q(book_cnt=2) | Q(book_cnt=2)).order_by('name')
- self.assertQuerysetEqual(
- qs,
- expected_results,
- lambda b: b.name
+ qs = (
+ Author.objects.annotate(book_cnt=Count("book"))
+ .exclude(Q(book_cnt=2) | Q(book_cnt=2))
+ .order_by("name")
)
+ self.assertQuerysetEqual(qs, expected_results, lambda b: b.name)
def test_name_filters(self):
- qs = Author.objects.annotate(Count('book')).filter(
- Q(book__count__exact=2) | Q(name='Adrian Holovaty')
- ).order_by('name')
+ qs = (
+ Author.objects.annotate(Count("book"))
+ .filter(Q(book__count__exact=2) | Q(name="Adrian Holovaty"))
+ .order_by("name")
+ )
self.assertQuerysetEqual(
- qs,
- ['Adrian Holovaty', 'Peter Norvig'],
- lambda b: b.name
+ qs, ["Adrian Holovaty", "Peter Norvig"], lambda b: b.name
)
def test_name_expressions(self):
# Aggregates are spotted correctly from F objects.
# Note that Adrian's age is 34 in the fixtures, and he has one book
# so both conditions match one author.
- qs = Author.objects.annotate(Count('book')).filter(
- Q(name='Peter Norvig') | Q(age=F('book__count') + 33)
- ).order_by('name')
+ qs = (
+ Author.objects.annotate(Count("book"))
+ .filter(Q(name="Peter Norvig") | Q(age=F("book__count") + 33))
+ .order_by("name")
+ )
self.assertQuerysetEqual(
- qs,
- ['Adrian Holovaty', 'Peter Norvig'],
- lambda b: b.name
+ qs, ["Adrian Holovaty", "Peter Norvig"], lambda b: b.name
)
def test_ticket_11293(self):
q1 = Q(price__gt=50)
q2 = Q(authors__count__gt=1)
- query = Book.objects.annotate(Count('authors')).filter(
- q1 | q2).order_by('pk')
+ query = Book.objects.annotate(Count("authors")).filter(q1 | q2).order_by("pk")
self.assertQuerysetEqual(
query,
[self.b1.pk, self.b4.pk, self.b5.pk, self.b6.pk],
- attrgetter('pk'),
+ attrgetter("pk"),
)
def test_ticket_11293_q_immutable(self):
@@ -1460,9 +1684,9 @@ class AggregationTests(TestCase):
Splitting a q object to parts for where/having doesn't alter
the original q-object.
"""
- q1 = Q(isbn='')
+ q1 = Q(isbn="")
q2 = Q(authors__count__gt=1)
- query = Book.objects.annotate(Count('authors'))
+ query = Book.objects.annotate(Count("authors"))
query.filter(q1 | q2)
self.assertEqual(len(q2.children), 1)
@@ -1470,65 +1694,80 @@ class AggregationTests(TestCase):
"""
An F() object referring to related column works correctly in group by.
"""
- qs = Book.objects.annotate(
- account=Count('authors')
- ).filter(
- account=F('publisher__num_awards')
+ qs = Book.objects.annotate(account=Count("authors")).filter(
+ account=F("publisher__num_awards")
)
self.assertQuerysetEqual(
- qs, ['Sams Teach Yourself Django in 24 Hours'],
- lambda b: b.name)
+ qs, ["Sams Teach Yourself Django in 24 Hours"], lambda b: b.name
+ )
def test_annotate_reserved_word(self):
"""
Regression #18333 - Ensure annotated column name is properly quoted.
"""
- vals = Book.objects.annotate(select=Count('authors__id')).aggregate(Sum('select'), Avg('select'))
- self.assertEqual(vals, {
- 'select__sum': 10,
- 'select__avg': Approximate(1.666, places=2),
- })
+ vals = Book.objects.annotate(select=Count("authors__id")).aggregate(
+ Sum("select"), Avg("select")
+ )
+ self.assertEqual(
+ vals,
+ {
+ "select__sum": 10,
+ "select__avg": Approximate(1.666, places=2),
+ },
+ )
def test_annotate_on_relation(self):
- book = Book.objects.annotate(avg_price=Avg('price'), publisher_name=F('publisher__name')).get(pk=self.b1.pk)
+ book = Book.objects.annotate(
+ avg_price=Avg("price"), publisher_name=F("publisher__name")
+ ).get(pk=self.b1.pk)
self.assertEqual(book.avg_price, 30.00)
self.assertEqual(book.publisher_name, "Apress")
def test_aggregate_on_relation(self):
# A query with an existing annotation aggregation on a relation should
# succeed.
- qs = Book.objects.annotate(avg_price=Avg('price')).aggregate(
- publisher_awards=Sum('publisher__num_awards')
+ qs = Book.objects.annotate(avg_price=Avg("price")).aggregate(
+ publisher_awards=Sum("publisher__num_awards")
)
- self.assertEqual(qs['publisher_awards'], 30)
+ self.assertEqual(qs["publisher_awards"], 30)
def test_annotate_distinct_aggregate(self):
# There are three books with rating of 4.0 and two of the books have
# the same price. Hence, the distinct removes one rating of 4.0
# from the results.
- vals1 = Book.objects.values('rating', 'price').distinct().aggregate(result=Sum('rating'))
- vals2 = Book.objects.aggregate(result=Sum('rating') - Value(4.0))
+ vals1 = (
+ Book.objects.values("rating", "price")
+ .distinct()
+ .aggregate(result=Sum("rating"))
+ )
+ vals2 = Book.objects.aggregate(result=Sum("rating") - Value(4.0))
self.assertEqual(vals1, vals2)
def test_annotate_values_list_flat(self):
"""Find ages that are shared by at least two authors."""
- qs = Author.objects.values_list('age', flat=True).annotate(age_count=Count('age')).filter(age_count__gt=1)
+ qs = (
+ Author.objects.values_list("age", flat=True)
+ .annotate(age_count=Count("age"))
+ .filter(age_count__gt=1)
+ )
self.assertSequenceEqual(qs, [29])
def test_allow_distinct(self):
class MyAggregate(Aggregate):
pass
- with self.assertRaisesMessage(TypeError, 'MyAggregate does not allow distinct'):
- MyAggregate('foo', distinct=True)
+
+ with self.assertRaisesMessage(TypeError, "MyAggregate does not allow distinct"):
+ MyAggregate("foo", distinct=True)
class DistinctAggregate(Aggregate):
allow_distinct = True
- DistinctAggregate('foo', distinct=True)
- @skipUnlessDBFeature('supports_subqueries_in_group_by')
+ DistinctAggregate("foo", distinct=True)
+
+ @skipUnlessDBFeature("supports_subqueries_in_group_by")
def test_having_subquery_select(self):
authors = Author.objects.filter(pk=self.a1.pk)
- books = Book.objects.annotate(Count('authors')).filter(
+ books = Book.objects.annotate(Count("authors")).filter(
Q(authors__in=authors) | Q(authors__count__gt=2)
)
self.assertEqual(set(books), {self.b1, self.b4})
@@ -1538,7 +1777,7 @@ class JoinPromotionTests(TestCase):
def test_ticket_21150(self):
b = Bravo.objects.create()
c = Charlie.objects.create(bravo=b)
- qs = Charlie.objects.select_related('alfa').annotate(Count('bravo__charlie'))
+ qs = Charlie.objects.select_related("alfa").annotate(Count("bravo__charlie"))
self.assertSequenceEqual(qs, [c])
self.assertIs(qs[0].alfa, None)
a = Alfa.objects.create()
@@ -1551,28 +1790,32 @@ class JoinPromotionTests(TestCase):
def test_existing_join_not_promoted(self):
# No promotion for existing joins
- qs = Charlie.objects.filter(alfa__name__isnull=False).annotate(Count('alfa__name'))
- self.assertIn(' INNER JOIN ', str(qs.query))
+ qs = Charlie.objects.filter(alfa__name__isnull=False).annotate(
+ Count("alfa__name")
+ )
+ self.assertIn(" INNER JOIN ", str(qs.query))
# Also, the existing join is unpromoted when doing filtering for already
# promoted join.
- qs = Charlie.objects.annotate(Count('alfa__name')).filter(alfa__name__isnull=False)
- self.assertIn(' INNER JOIN ', str(qs.query))
+ qs = Charlie.objects.annotate(Count("alfa__name")).filter(
+ alfa__name__isnull=False
+ )
+ self.assertIn(" INNER JOIN ", str(qs.query))
# But, as the join is nullable first use by annotate will be LOUTER
- qs = Charlie.objects.annotate(Count('alfa__name'))
- self.assertIn(' LEFT OUTER JOIN ', str(qs.query))
+ qs = Charlie.objects.annotate(Count("alfa__name"))
+ self.assertIn(" LEFT OUTER JOIN ", str(qs.query))
def test_non_nullable_fk_not_promoted(self):
- qs = Book.objects.annotate(Count('contact__name'))
- self.assertIn(' INNER JOIN ', str(qs.query))
+ qs = Book.objects.annotate(Count("contact__name"))
+ self.assertIn(" INNER JOIN ", str(qs.query))
class SelfReferentialFKTests(TestCase):
def test_ticket_24748(self):
- t1 = SelfRefFK.objects.create(name='t1')
- SelfRefFK.objects.create(name='t2', parent=t1)
- SelfRefFK.objects.create(name='t3', parent=t1)
+ t1 = SelfRefFK.objects.create(name="t1")
+ SelfRefFK.objects.create(name="t2", parent=t1)
+ SelfRefFK.objects.create(name="t3", parent=t1)
self.assertQuerysetEqual(
- SelfRefFK.objects.annotate(num_children=Count('children')).order_by('name'),
- [('t1', 2), ('t2', 0), ('t3', 0)],
- lambda x: (x.name, x.num_children)
+ SelfRefFK.objects.annotate(num_children=Count("children")).order_by("name"),
+ [("t1", 2), ("t2", 0), ("t3", 0)],
+ lambda x: (x.name, x.num_children),
)