summaryrefslogtreecommitdiff
path: root/tests/annotations
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/annotations
parentf68fa8b45dfac545cfc4111d4e52804c86db68d3 (diff)
downloaddjango-9c19aff7c7561e3a82978a272ecdaad40dda5c00.tar.gz
Refs #33476 -- Reformatted code with Black.
Diffstat (limited to 'tests/annotations')
-rw-r--r--tests/annotations/models.py6
-rw-r--r--tests/annotations/tests.py1234
2 files changed, 778 insertions, 462 deletions
diff --git a/tests/annotations/models.py b/tests/annotations/models.py
index 500240df9b..fbb9ca6988 100644
--- a/tests/annotations/models.py
+++ b/tests/annotations/models.py
@@ -4,7 +4,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):
@@ -19,7 +19,7 @@ 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()
@@ -29,7 +29,7 @@ class Store(models.Model):
books = models.ManyToManyField(Book)
original_opening = models.DateTimeField()
friday_night_closing = models.TimeField()
- area = models.IntegerField(null=True, db_column='surface')
+ area = models.IntegerField(null=True, db_column="surface")
class DepartmentStore(Store):
diff --git a/tests/annotations/tests.py b/tests/annotations/tests.py
index 1755a9f435..b4954684aa 100644
--- a/tests/annotations/tests.py
+++ b/tests/annotations/tests.py
@@ -3,35 +3,55 @@ from decimal import Decimal
from django.core.exceptions import FieldDoesNotExist, FieldError
from django.db.models import (
- BooleanField, Case, CharField, Count, DateTimeField, DecimalField, Exists,
- ExpressionWrapper, F, FloatField, Func, IntegerField, Max, OuterRef, Q,
- Subquery, Sum, Value, When,
+ BooleanField,
+ Case,
+ CharField,
+ Count,
+ DateTimeField,
+ DecimalField,
+ Exists,
+ ExpressionWrapper,
+ F,
+ FloatField,
+ Func,
+ IntegerField,
+ Max,
+ OuterRef,
+ Q,
+ Subquery,
+ Sum,
+ Value,
+ When,
)
from django.db.models.expressions import RawSQL
-from django.db.models.functions import (
- Coalesce, ExtractYear, Floor, Length, Lower, Trim,
-)
+from django.db.models.functions import Coalesce, ExtractYear, Floor, Length, Lower, Trim
from django.test import TestCase, skipUnlessDBFeature
from django.test.utils import register_lookup
from .models import (
- Author, Book, Company, DepartmentStore, Employee, Publisher, Store, Ticket,
+ Author,
+ Book,
+ Company,
+ DepartmentStore,
+ Employee,
+ Publisher,
+ Store,
+ Ticket,
)
class NonAggregateAnnotationTestCase(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)
@@ -41,41 +61,71 @@ class NonAggregateAnnotationTestCase(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 = Book.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)
+ 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),
)
cls.b6 = Book.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)
+ 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),
)
cls.b1.authors.add(cls.a1, cls.a2)
cls.b2.authors.add(cls.a3)
@@ -85,19 +135,19 @@ class NonAggregateAnnotationTestCase(TestCase):
cls.b6.authors.add(cls.a8)
cls.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),
)
cls.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),
)
cls.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),
)
cls.s1.books.add(cls.b1, cls.b2, cls.b3, cls.b4, cls.b5, cls.b6)
cls.s2.books.add(cls.b1, cls.b3, cls.b5, cls.b6)
@@ -109,42 +159,45 @@ class NonAggregateAnnotationTestCase(TestCase):
self.assertEqual(book.is_book, 1)
def test_basic_f_annotation(self):
- books = Book.objects.annotate(another_rating=F('rating'))
+ books = Book.objects.annotate(another_rating=F("rating"))
for book in books:
self.assertEqual(book.another_rating, book.rating)
def test_joined_annotation(self):
- books = Book.objects.select_related('publisher').annotate(
- num_awards=F('publisher__num_awards'))
+ books = Book.objects.select_related("publisher").annotate(
+ num_awards=F("publisher__num_awards")
+ )
for book in books:
self.assertEqual(book.num_awards, book.publisher.num_awards)
def test_joined_transformed_annotation(self):
- Employee.objects.bulk_create([
- Employee(
- first_name='John',
- last_name='Doe',
- age=18,
- store=self.s1,
- salary=15000,
- ),
- Employee(
- first_name='Jane',
- last_name='Jones',
- age=30,
- store=self.s2,
- salary=30000,
- ),
- Employee(
- first_name='Jo',
- last_name='Smith',
- age=55,
- store=self.s3,
- salary=50000,
- ),
- ])
+ Employee.objects.bulk_create(
+ [
+ Employee(
+ first_name="John",
+ last_name="Doe",
+ age=18,
+ store=self.s1,
+ salary=15000,
+ ),
+ Employee(
+ first_name="Jane",
+ last_name="Jones",
+ age=30,
+ store=self.s2,
+ salary=30000,
+ ),
+ Employee(
+ first_name="Jo",
+ last_name="Smith",
+ age=55,
+ store=self.s3,
+ salary=50000,
+ ),
+ ]
+ )
employees = Employee.objects.annotate(
- store_opened_year=F('store__original_opening__year'),
+ store_opened_year=F("store__original_opening__year"),
)
for employee in employees:
self.assertEqual(
@@ -154,28 +207,31 @@ class NonAggregateAnnotationTestCase(TestCase):
def test_custom_transform_annotation(self):
with register_lookup(DecimalField, Floor):
- books = Book.objects.annotate(floor_price=F('price__floor'))
-
- self.assertSequenceEqual(books.values_list('pk', 'floor_price'), [
- (self.b1.pk, 30),
- (self.b2.pk, 23),
- (self.b3.pk, 29),
- (self.b4.pk, 29),
- (self.b5.pk, 82),
- (self.b6.pk, 75),
- ])
+ books = Book.objects.annotate(floor_price=F("price__floor"))
+
+ self.assertSequenceEqual(
+ books.values_list("pk", "floor_price"),
+ [
+ (self.b1.pk, 30),
+ (self.b2.pk, 23),
+ (self.b3.pk, 29),
+ (self.b4.pk, 29),
+ (self.b5.pk, 82),
+ (self.b6.pk, 75),
+ ],
+ )
def test_chaining_transforms(self):
- Company.objects.create(name=' Django Software Foundation ')
- Company.objects.create(name='Yahoo')
+ Company.objects.create(name=" Django Software Foundation ")
+ Company.objects.create(name="Yahoo")
with register_lookup(CharField, Trim), register_lookup(CharField, Length):
- for expr in [Length('name__trim'), F('name__trim__length')]:
+ for expr in [Length("name__trim"), F("name__trim__length")]:
with self.subTest(expr=expr):
self.assertCountEqual(
- Company.objects.annotate(length=expr).values('name', 'length'),
+ Company.objects.annotate(length=expr).values("name", "length"),
[
- {'name': ' Django Software Foundation ', 'length': 26},
- {'name': 'Yahoo', 'length': 5},
+ {"name": " Django Software Foundation ", "length": 26},
+ {"name": "Yahoo", "length": 5},
],
)
@@ -185,14 +241,18 @@ class NonAggregateAnnotationTestCase(TestCase):
expires = datetime.datetime(2015, 3, 20, 14, 0, 0) + duration
Ticket.objects.create(active_at=active, duration=duration)
t = Ticket.objects.annotate(
- expires=ExpressionWrapper(F('active_at') + F('duration'), output_field=DateTimeField())
+ expires=ExpressionWrapper(
+ F("active_at") + F("duration"), output_field=DateTimeField()
+ )
).first()
self.assertEqual(t.expires, expires)
def test_mixed_type_annotation_numbers(self):
test = self.b1
b = Book.objects.annotate(
- combined=ExpressionWrapper(F('pages') + F('rating'), output_field=IntegerField())
+ combined=ExpressionWrapper(
+ F("pages") + F("rating"), output_field=IntegerField()
+ )
).get(isbn=test.isbn)
combined = int(test.pages + test.rating)
self.assertEqual(b.combined, combined)
@@ -205,7 +265,9 @@ class NonAggregateAnnotationTestCase(TestCase):
self.assertTrue(all(not book.selected for book in books))
books = Book.objects.annotate(
- selected=ExpressionWrapper(Q(pk__in=Book.objects.none()), output_field=BooleanField())
+ selected=ExpressionWrapper(
+ Q(pk__in=Book.objects.none()), output_field=BooleanField()
+ )
)
self.assertEqual(len(books), Book.objects.count())
self.assertTrue(all(not book.selected for book in books))
@@ -218,112 +280,137 @@ class NonAggregateAnnotationTestCase(TestCase):
self.assertTrue(all(book.selected for book in books))
def test_full_expression_annotation_with_aggregation(self):
- qs = Book.objects.filter(isbn='159059725').annotate(
+ qs = Book.objects.filter(isbn="159059725").annotate(
selected=ExpressionWrapper(~Q(pk__in=[]), output_field=BooleanField()),
- rating_count=Count('rating'),
+ rating_count=Count("rating"),
)
self.assertEqual([book.rating_count for book in qs], [1])
def test_aggregate_over_full_expression_annotation(self):
qs = Book.objects.annotate(
selected=ExpressionWrapper(~Q(pk__in=[]), output_field=BooleanField()),
- ).aggregate(Sum('selected'))
- self.assertEqual(qs['selected__sum'], Book.objects.count())
+ ).aggregate(Sum("selected"))
+ self.assertEqual(qs["selected__sum"], Book.objects.count())
def test_empty_queryset_annotation(self):
- qs = Author.objects.annotate(
- empty=Subquery(Author.objects.values('id').none())
- )
+ qs = Author.objects.annotate(empty=Subquery(Author.objects.values("id").none()))
self.assertIsNone(qs.first().empty)
def test_annotate_with_aggregation(self):
- books = Book.objects.annotate(is_book=Value(1), rating_count=Count('rating'))
+ books = Book.objects.annotate(is_book=Value(1), rating_count=Count("rating"))
for book in books:
self.assertEqual(book.is_book, 1)
self.assertEqual(book.rating_count, 1)
def test_combined_expression_annotation_with_aggregation(self):
book = Book.objects.annotate(
- combined=ExpressionWrapper(Value(3) * Value(4), output_field=IntegerField()),
- rating_count=Count('rating'),
+ combined=ExpressionWrapper(
+ Value(3) * Value(4), output_field=IntegerField()
+ ),
+ rating_count=Count("rating"),
).first()
self.assertEqual(book.combined, 12)
self.assertEqual(book.rating_count, 1)
def test_combined_f_expression_annotation_with_aggregation(self):
- book = Book.objects.filter(isbn='159059725').annotate(
- combined=ExpressionWrapper(F('price') * F('pages'), output_field=FloatField()),
- rating_count=Count('rating'),
- ).first()
+ book = (
+ Book.objects.filter(isbn="159059725")
+ .annotate(
+ combined=ExpressionWrapper(
+ F("price") * F("pages"), output_field=FloatField()
+ ),
+ rating_count=Count("rating"),
+ )
+ .first()
+ )
self.assertEqual(book.combined, 13410.0)
self.assertEqual(book.rating_count, 1)
- @skipUnlessDBFeature('supports_boolean_expr_in_select_clause')
+ @skipUnlessDBFeature("supports_boolean_expr_in_select_clause")
def test_q_expression_annotation_with_aggregation(self):
- book = Book.objects.filter(isbn='159059725').annotate(
- isnull_pubdate=ExpressionWrapper(
- Q(pubdate__isnull=True),
- output_field=BooleanField(),
- ),
- rating_count=Count('rating'),
- ).first()
+ book = (
+ Book.objects.filter(isbn="159059725")
+ .annotate(
+ isnull_pubdate=ExpressionWrapper(
+ Q(pubdate__isnull=True),
+ output_field=BooleanField(),
+ ),
+ rating_count=Count("rating"),
+ )
+ .first()
+ )
self.assertIs(book.isnull_pubdate, False)
self.assertEqual(book.rating_count, 1)
- @skipUnlessDBFeature('supports_boolean_expr_in_select_clause')
+ @skipUnlessDBFeature("supports_boolean_expr_in_select_clause")
def test_grouping_by_q_expression_annotation(self):
- authors = Author.objects.annotate(
- under_40=ExpressionWrapper(Q(age__lt=40), output_field=BooleanField()),
- ).values('under_40').annotate(
- count_id=Count('id'),
- ).values('under_40', 'count_id')
- self.assertCountEqual(authors, [
- {'under_40': False, 'count_id': 3},
- {'under_40': True, 'count_id': 6},
- ])
+ authors = (
+ Author.objects.annotate(
+ under_40=ExpressionWrapper(Q(age__lt=40), output_field=BooleanField()),
+ )
+ .values("under_40")
+ .annotate(
+ count_id=Count("id"),
+ )
+ .values("under_40", "count_id")
+ )
+ self.assertCountEqual(
+ authors,
+ [
+ {"under_40": False, "count_id": 3},
+ {"under_40": True, "count_id": 6},
+ ],
+ )
def test_aggregate_over_annotation(self):
- agg = Author.objects.annotate(other_age=F('age')).aggregate(otherage_sum=Sum('other_age'))
- other_agg = Author.objects.aggregate(age_sum=Sum('age'))
- self.assertEqual(agg['otherage_sum'], other_agg['age_sum'])
+ agg = Author.objects.annotate(other_age=F("age")).aggregate(
+ otherage_sum=Sum("other_age")
+ )
+ other_agg = Author.objects.aggregate(age_sum=Sum("age"))
+ self.assertEqual(agg["otherage_sum"], other_agg["age_sum"])
- @skipUnlessDBFeature('can_distinct_on_fields')
+ @skipUnlessDBFeature("can_distinct_on_fields")
def test_distinct_on_with_annotation(self):
store = Store.objects.create(
- name='test store',
+ name="test store",
original_opening=datetime.datetime.now(),
friday_night_closing=datetime.time(21, 00, 00),
)
names = [
- 'Theodore Roosevelt',
- 'Eleanor Roosevelt',
- 'Franklin Roosevelt',
- 'Ned Stark',
- 'Catelyn Stark',
+ "Theodore Roosevelt",
+ "Eleanor Roosevelt",
+ "Franklin Roosevelt",
+ "Ned Stark",
+ "Catelyn Stark",
]
for name in names:
Employee.objects.create(
store=store,
first_name=name.split()[0],
last_name=name.split()[1],
- age=30, salary=2000,
+ age=30,
+ salary=2000,
)
people = Employee.objects.annotate(
- name_lower=Lower('last_name'),
- ).distinct('name_lower')
+ name_lower=Lower("last_name"),
+ ).distinct("name_lower")
- self.assertEqual({p.last_name for p in people}, {'Stark', 'Roosevelt'})
+ self.assertEqual({p.last_name for p in people}, {"Stark", "Roosevelt"})
self.assertEqual(len(people), 2)
people2 = Employee.objects.annotate(
- test_alias=F('store__name'),
- ).distinct('test_alias')
+ test_alias=F("store__name"),
+ ).distinct("test_alias")
self.assertEqual(len(people2), 1)
- lengths = Employee.objects.annotate(
- name_len=Length('first_name'),
- ).distinct('name_len').values_list('name_len', flat=True)
+ lengths = (
+ Employee.objects.annotate(
+ name_len=Length("first_name"),
+ )
+ .distinct("name_len")
+ .values_list("name_len", flat=True)
+ )
self.assertCountEqual(lengths, [3, 7, 8])
def test_filter_annotation(self):
@@ -332,83 +419,104 @@ class NonAggregateAnnotationTestCase(TestCase):
self.assertEqual(book.is_book, 1)
def test_filter_annotation_with_f(self):
- books = Book.objects.annotate(
- other_rating=F('rating')
- ).filter(other_rating=3.5)
+ books = Book.objects.annotate(other_rating=F("rating")).filter(other_rating=3.5)
for book in books:
self.assertEqual(book.other_rating, 3.5)
def test_filter_annotation_with_double_f(self):
- books = Book.objects.annotate(
- other_rating=F('rating')
- ).filter(other_rating=F('rating'))
+ books = Book.objects.annotate(other_rating=F("rating")).filter(
+ other_rating=F("rating")
+ )
for book in books:
self.assertEqual(book.other_rating, book.rating)
def test_filter_agg_with_double_f(self):
- books = Book.objects.annotate(
- sum_rating=Sum('rating')
- ).filter(sum_rating=F('sum_rating'))
+ books = Book.objects.annotate(sum_rating=Sum("rating")).filter(
+ sum_rating=F("sum_rating")
+ )
for book in books:
self.assertEqual(book.sum_rating, book.rating)
def test_filter_wrong_annotation(self):
- with self.assertRaisesMessage(FieldError, "Cannot resolve keyword 'nope' into field."):
- list(Book.objects.annotate(
- sum_rating=Sum('rating')
- ).filter(sum_rating=F('nope')))
+ with self.assertRaisesMessage(
+ FieldError, "Cannot resolve keyword 'nope' into field."
+ ):
+ list(
+ Book.objects.annotate(sum_rating=Sum("rating")).filter(
+ sum_rating=F("nope")
+ )
+ )
def test_decimal_annotation(self):
- salary = Decimal(10) ** -Employee._meta.get_field('salary').decimal_places
+ salary = Decimal(10) ** -Employee._meta.get_field("salary").decimal_places
Employee.objects.create(
- first_name='Max',
- last_name='Paine',
+ first_name="Max",
+ last_name="Paine",
store=Store.objects.first(),
age=23,
salary=salary,
)
self.assertEqual(
- Employee.objects.annotate(new_salary=F('salary') / 10).get().new_salary,
+ Employee.objects.annotate(new_salary=F("salary") / 10).get().new_salary,
salary / 10,
)
def test_filter_decimal_annotation(self):
- qs = Book.objects.annotate(new_price=F('price') + 1).filter(new_price=Decimal(31)).values_list('new_price')
+ qs = (
+ Book.objects.annotate(new_price=F("price") + 1)
+ .filter(new_price=Decimal(31))
+ .values_list("new_price")
+ )
self.assertEqual(qs.get(), (Decimal(31),))
def test_combined_annotation_commutative(self):
- book1 = Book.objects.annotate(adjusted_rating=F('rating') + 2).get(pk=self.b1.pk)
- book2 = Book.objects.annotate(adjusted_rating=2 + F('rating')).get(pk=self.b1.pk)
+ book1 = Book.objects.annotate(adjusted_rating=F("rating") + 2).get(
+ pk=self.b1.pk
+ )
+ book2 = Book.objects.annotate(adjusted_rating=2 + F("rating")).get(
+ pk=self.b1.pk
+ )
self.assertEqual(book1.adjusted_rating, book2.adjusted_rating)
- book1 = Book.objects.annotate(adjusted_rating=F('rating') + None).get(pk=self.b1.pk)
- book2 = Book.objects.annotate(adjusted_rating=None + F('rating')).get(pk=self.b1.pk)
+ book1 = Book.objects.annotate(adjusted_rating=F("rating") + None).get(
+ pk=self.b1.pk
+ )
+ book2 = Book.objects.annotate(adjusted_rating=None + F("rating")).get(
+ pk=self.b1.pk
+ )
self.assertEqual(book1.adjusted_rating, book2.adjusted_rating)
def test_update_with_annotation(self):
book_preupdate = Book.objects.get(pk=self.b2.pk)
- Book.objects.annotate(other_rating=F('rating') - 1).update(rating=F('other_rating'))
+ Book.objects.annotate(other_rating=F("rating") - 1).update(
+ rating=F("other_rating")
+ )
book_postupdate = Book.objects.get(pk=self.b2.pk)
self.assertEqual(book_preupdate.rating - 1, book_postupdate.rating)
def test_annotation_with_m2m(self):
- books = Book.objects.annotate(author_age=F('authors__age')).filter(pk=self.b1.pk).order_by('author_age')
+ books = (
+ Book.objects.annotate(author_age=F("authors__age"))
+ .filter(pk=self.b1.pk)
+ .order_by("author_age")
+ )
self.assertEqual(books[0].author_age, 34)
self.assertEqual(books[1].author_age, 35)
def test_annotation_reverse_m2m(self):
- books = Book.objects.annotate(
- store_name=F('store__name'),
- ).filter(
- name='Practical Django Projects',
- ).order_by('store_name')
+ books = (
+ Book.objects.annotate(
+ store_name=F("store__name"),
+ )
+ .filter(
+ name="Practical Django Projects",
+ )
+ .order_by("store_name")
+ )
self.assertQuerysetEqual(
- books, [
- 'Amazon.com',
- 'Books.com',
- 'Mamma and Pappa\'s Books'
- ],
- lambda b: b.store_name
+ books,
+ ["Amazon.com", "Books.com", "Mamma and Pappa's Books"],
+ lambda b: b.store_name,
)
def test_values_annotation(self):
@@ -417,33 +525,39 @@ class NonAggregateAnnotationTestCase(TestCase):
and contribute to an existing values clause.
"""
# annotate references a field in values()
- qs = Book.objects.values('rating').annotate(other_rating=F('rating') - 1)
+ qs = Book.objects.values("rating").annotate(other_rating=F("rating") - 1)
book = qs.get(pk=self.b1.pk)
- self.assertEqual(book['rating'] - 1, book['other_rating'])
+ self.assertEqual(book["rating"] - 1, book["other_rating"])
# filter refs the annotated value
book = qs.get(other_rating=4)
- self.assertEqual(book['other_rating'], 4)
+ self.assertEqual(book["other_rating"], 4)
# can annotate an existing values with a new field
- book = qs.annotate(other_isbn=F('isbn')).get(other_rating=4)
- self.assertEqual(book['other_rating'], 4)
- self.assertEqual(book['other_isbn'], '155860191')
+ book = qs.annotate(other_isbn=F("isbn")).get(other_rating=4)
+ self.assertEqual(book["other_rating"], 4)
+ self.assertEqual(book["other_isbn"], "155860191")
def test_values_with_pk_annotation(self):
# annotate references a field in values() with pk
- publishers = Publisher.objects.values('id', 'book__rating').annotate(total=Sum('book__rating'))
+ publishers = Publisher.objects.values("id", "book__rating").annotate(
+ total=Sum("book__rating")
+ )
for publisher in publishers.filter(pk=self.p1.pk):
- self.assertEqual(publisher['book__rating'], publisher['total'])
+ self.assertEqual(publisher["book__rating"], publisher["total"])
- @skipUnlessDBFeature('allows_group_by_pk')
+ @skipUnlessDBFeature("allows_group_by_pk")
def test_rawsql_group_by_collapse(self):
- raw = RawSQL('SELECT MIN(id) FROM annotations_book', [])
- qs = Author.objects.values('id').annotate(
- min_book_id=raw,
- count_friends=Count('friends'),
- ).order_by()
- _, _, group_by = qs.query.get_compiler(using='default').pre_sql_setup()
+ raw = RawSQL("SELECT MIN(id) FROM annotations_book", [])
+ qs = (
+ Author.objects.values("id")
+ .annotate(
+ min_book_id=raw,
+ count_friends=Count("friends"),
+ )
+ .order_by()
+ )
+ _, _, group_by = qs.query.get_compiler(using="default").pre_sql_setup()
self.assertEqual(len(group_by), 1)
self.assertNotEqual(raw, group_by[0])
@@ -452,15 +566,17 @@ class NonAggregateAnnotationTestCase(TestCase):
Deferred attributes can be referenced by an annotation,
but they are not themselves deferred, and cannot be deferred.
"""
- qs = Book.objects.defer('rating').annotate(other_rating=F('rating') - 1)
+ qs = Book.objects.defer("rating").annotate(other_rating=F("rating") - 1)
with self.assertNumQueries(2):
book = qs.get(other_rating=4)
self.assertEqual(book.rating, 5)
self.assertEqual(book.other_rating, 4)
- with self.assertRaisesMessage(FieldDoesNotExist, "Book has no field named 'other_rating'"):
- book = qs.defer('other_rating').get(other_rating=4)
+ with self.assertRaisesMessage(
+ FieldDoesNotExist, "Book has no field named 'other_rating'"
+ ):
+ book = qs.defer("other_rating").get(other_rating=4)
def test_mti_annotations(self):
"""
@@ -468,80 +584,108 @@ class NonAggregateAnnotationTestCase(TestCase):
annotated field.
"""
d = DepartmentStore.objects.create(
- name='Angus & Robinson',
+ name="Angus & Robinson",
original_opening=datetime.date(2014, 3, 8),
friday_night_closing=datetime.time(21, 00, 00),
- chain='Westfield'
+ chain="Westfield",
)
books = Book.objects.filter(rating__gt=4)
for b in books:
d.books.add(b)
- qs = DepartmentStore.objects.annotate(
- other_name=F('name'),
- other_chain=F('chain'),
- is_open=Value(True, BooleanField()),
- book_isbn=F('books__isbn')
- ).order_by('book_isbn').filter(chain='Westfield')
+ qs = (
+ DepartmentStore.objects.annotate(
+ other_name=F("name"),
+ other_chain=F("chain"),
+ is_open=Value(True, BooleanField()),
+ book_isbn=F("books__isbn"),
+ )
+ .order_by("book_isbn")
+ .filter(chain="Westfield")
+ )
self.assertQuerysetEqual(
- qs, [
- ('Angus & Robinson', 'Westfield', True, '155860191'),
- ('Angus & Robinson', 'Westfield', True, '159059725')
+ qs,
+ [
+ ("Angus & Robinson", "Westfield", True, "155860191"),
+ ("Angus & Robinson", "Westfield", True, "159059725"),
],
- lambda d: (d.other_name, d.other_chain, d.is_open, d.book_isbn)
+ lambda d: (d.other_name, d.other_chain, d.is_open, d.book_isbn),
)
def test_null_annotation(self):
"""
Annotating None onto a model round-trips
"""
- book = Book.objects.annotate(no_value=Value(None, output_field=IntegerField())).first()
+ book = Book.objects.annotate(
+ no_value=Value(None, output_field=IntegerField())
+ ).first()
self.assertIsNone(book.no_value)
def test_order_by_annotation(self):
- authors = Author.objects.annotate(other_age=F('age')).order_by('other_age')
+ authors = Author.objects.annotate(other_age=F("age")).order_by("other_age")
self.assertQuerysetEqual(
- authors, [
- 25, 29, 29, 34, 35, 37, 45, 46, 57,
+ authors,
+ [
+ 25,
+ 29,
+ 29,
+ 34,
+ 35,
+ 37,
+ 45,
+ 46,
+ 57,
],
- lambda a: a.other_age
+ lambda a: a.other_age,
)
def test_order_by_aggregate(self):
- authors = Author.objects.values('age').annotate(age_count=Count('age')).order_by('age_count', 'age')
+ authors = (
+ Author.objects.values("age")
+ .annotate(age_count=Count("age"))
+ .order_by("age_count", "age")
+ )
self.assertQuerysetEqual(
- authors, [
- (25, 1), (34, 1), (35, 1), (37, 1), (45, 1), (46, 1), (57, 1), (29, 2),
+ authors,
+ [
+ (25, 1),
+ (34, 1),
+ (35, 1),
+ (37, 1),
+ (45, 1),
+ (46, 1),
+ (57, 1),
+ (29, 2),
],
- lambda a: (a['age'], a['age_count'])
+ lambda a: (a["age"], a["age_count"]),
)
def test_raw_sql_with_inherited_field(self):
DepartmentStore.objects.create(
- name='Angus & Robinson',
+ name="Angus & Robinson",
original_opening=datetime.date(2014, 3, 8),
friday_night_closing=datetime.time(21),
- chain='Westfield',
+ chain="Westfield",
area=123,
)
tests = (
- ('name', 'Angus & Robinson'),
- ('surface', 123),
- ("case when name='Angus & Robinson' then chain else name end", 'Westfield'),
+ ("name", "Angus & Robinson"),
+ ("surface", 123),
+ ("case when name='Angus & Robinson' then chain else name end", "Westfield"),
)
for sql, expected_result in tests:
with self.subTest(sql=sql):
self.assertSequenceEqual(
DepartmentStore.objects.annotate(
annotation=RawSQL(sql, ()),
- ).values_list('annotation', flat=True),
+ ).values_list("annotation", flat=True),
[expected_result],
)
def test_annotate_exists(self):
- authors = Author.objects.annotate(c=Count('id')).filter(c__gt=1)
+ authors = Author.objects.annotate(c=Count("id")).filter(c__gt=1)
self.assertFalse(authors.exists())
def test_column_field_ordering(self):
@@ -555,111 +699,203 @@ class NonAggregateAnnotationTestCase(TestCase):
4. model_related_fields
"""
store = Store.objects.first()
- Employee.objects.create(id=1, first_name='Max', manager=True, last_name='Paine',
- store=store, age=23, salary=Decimal(50000.00))
- Employee.objects.create(id=2, first_name='Buffy', manager=False, last_name='Summers',
- store=store, age=18, salary=Decimal(40000.00))
+ Employee.objects.create(
+ id=1,
+ first_name="Max",
+ manager=True,
+ last_name="Paine",
+ store=store,
+ age=23,
+ salary=Decimal(50000.00),
+ )
+ Employee.objects.create(
+ id=2,
+ first_name="Buffy",
+ manager=False,
+ last_name="Summers",
+ store=store,
+ age=18,
+ salary=Decimal(40000.00),
+ )
- qs = Employee.objects.extra(
- select={'random_value': '42'}
- ).select_related('store').annotate(
- annotated_value=Value(17),
+ qs = (
+ Employee.objects.extra(select={"random_value": "42"})
+ .select_related("store")
+ .annotate(
+ annotated_value=Value(17),
+ )
)
rows = [
- (1, 'Max', True, 42, 'Paine', 23, Decimal(50000.00), store.name, 17),
- (2, 'Buffy', False, 42, 'Summers', 18, Decimal(40000.00), store.name, 17)
+ (1, "Max", True, 42, "Paine", 23, Decimal(50000.00), store.name, 17),
+ (2, "Buffy", False, 42, "Summers", 18, Decimal(40000.00), store.name, 17),
]
self.assertQuerysetEqual(
- qs.order_by('id'), rows,
+ qs.order_by("id"),
+ rows,
lambda e: (
- e.id, e.first_name, e.manager, e.random_value, e.last_name, e.age,
- e.salary, e.store.name, e.annotated_value))
+ e.id,
+ e.first_name,
+ e.manager,
+ e.random_value,
+ e.last_name,
+ e.age,
+ e.salary,
+ e.store.name,
+ e.annotated_value,
+ ),
+ )
def test_column_field_ordering_with_deferred(self):
store = Store.objects.first()
- Employee.objects.create(id=1, first_name='Max', manager=True, last_name='Paine',
- store=store, age=23, salary=Decimal(50000.00))
- Employee.objects.create(id=2, first_name='Buffy', manager=False, last_name='Summers',
- store=store, age=18, salary=Decimal(40000.00))
+ Employee.objects.create(
+ id=1,
+ first_name="Max",
+ manager=True,
+ last_name="Paine",
+ store=store,
+ age=23,
+ salary=Decimal(50000.00),
+ )
+ Employee.objects.create(
+ id=2,
+ first_name="Buffy",
+ manager=False,
+ last_name="Summers",
+ store=store,
+ age=18,
+ salary=Decimal(40000.00),
+ )
- qs = Employee.objects.extra(
- select={'random_value': '42'}
- ).select_related('store').annotate(
- annotated_value=Value(17),
+ qs = (
+ Employee.objects.extra(select={"random_value": "42"})
+ .select_related("store")
+ .annotate(
+ annotated_value=Value(17),
+ )
)
rows = [
- (1, 'Max', True, 42, 'Paine', 23, Decimal(50000.00), store.name, 17),
- (2, 'Buffy', False, 42, 'Summers', 18, Decimal(40000.00), store.name, 17)
+ (1, "Max", True, 42, "Paine", 23, Decimal(50000.00), store.name, 17),
+ (2, "Buffy", False, 42, "Summers", 18, Decimal(40000.00), store.name, 17),
]
# and we respect deferred columns!
self.assertQuerysetEqual(
- qs.defer('age').order_by('id'), rows,
+ qs.defer("age").order_by("id"),
+ rows,
lambda e: (
- e.id, e.first_name, e.manager, e.random_value, e.last_name, e.age,
- e.salary, e.store.name, e.annotated_value))
+ e.id,
+ e.first_name,
+ e.manager,
+ e.random_value,
+ e.last_name,
+ e.age,
+ e.salary,
+ e.store.name,
+ e.annotated_value,
+ ),
+ )
def test_custom_functions(self):
- Company(name='Apple', motto=None, ticker_name='APPL', description='Beautiful Devices').save()
- Company(name='Django Software Foundation', motto=None, ticker_name=None, description=None).save()
- Company(name='Google', motto='Do No Evil', ticker_name='GOOG', description='Internet Company').save()
- Company(name='Yahoo', motto=None, ticker_name=None, description='Internet Company').save()
+ Company(
+ name="Apple",
+ motto=None,
+ ticker_name="APPL",
+ description="Beautiful Devices",
+ ).save()
+ Company(
+ name="Django Software Foundation",
+ motto=None,
+ ticker_name=None,
+ description=None,
+ ).save()
+ Company(
+ name="Google",
+ motto="Do No Evil",
+ ticker_name="GOOG",
+ description="Internet Company",
+ ).save()
+ Company(
+ name="Yahoo", motto=None, ticker_name=None, description="Internet Company"
+ ).save()
qs = Company.objects.annotate(
tagline=Func(
- F('motto'),
- F('ticker_name'),
- F('description'),
- Value('No Tag'),
- function='COALESCE'
+ F("motto"),
+ F("ticker_name"),
+ F("description"),
+ Value("No Tag"),
+ function="COALESCE",
)
- ).order_by('name')
+ ).order_by("name")
self.assertQuerysetEqual(
- qs, [
- ('Apple', 'APPL'),
- ('Django Software Foundation', 'No Tag'),
- ('Google', 'Do No Evil'),
- ('Yahoo', 'Internet Company')
+ qs,
+ [
+ ("Apple", "APPL"),
+ ("Django Software Foundation", "No Tag"),
+ ("Google", "Do No Evil"),
+ ("Yahoo", "Internet Company"),
],
- lambda c: (c.name, c.tagline)
+ lambda c: (c.name, c.tagline),
)
def test_custom_functions_can_ref_other_functions(self):
- Company(name='Apple', motto=None, ticker_name='APPL', description='Beautiful Devices').save()
- Company(name='Django Software Foundation', motto=None, ticker_name=None, description=None).save()
- Company(name='Google', motto='Do No Evil', ticker_name='GOOG', description='Internet Company').save()
- Company(name='Yahoo', motto=None, ticker_name=None, description='Internet Company').save()
+ Company(
+ name="Apple",
+ motto=None,
+ ticker_name="APPL",
+ description="Beautiful Devices",
+ ).save()
+ Company(
+ name="Django Software Foundation",
+ motto=None,
+ ticker_name=None,
+ description=None,
+ ).save()
+ Company(
+ name="Google",
+ motto="Do No Evil",
+ ticker_name="GOOG",
+ description="Internet Company",
+ ).save()
+ Company(
+ name="Yahoo", motto=None, ticker_name=None, description="Internet Company"
+ ).save()
class Lower(Func):
- function = 'LOWER'
+ function = "LOWER"
- qs = Company.objects.annotate(
- tagline=Func(
- F('motto'),
- F('ticker_name'),
- F('description'),
- Value('No Tag'),
- function='COALESCE',
+ qs = (
+ Company.objects.annotate(
+ tagline=Func(
+ F("motto"),
+ F("ticker_name"),
+ F("description"),
+ Value("No Tag"),
+ function="COALESCE",
+ )
)
- ).annotate(
- tagline_lower=Lower(F('tagline')),
- ).order_by('name')
+ .annotate(
+ tagline_lower=Lower(F("tagline")),
+ )
+ .order_by("name")
+ )
# LOWER function supported by:
# oracle, postgres, mysql, sqlite, sqlserver
self.assertQuerysetEqual(
- qs, [
- ('Apple', 'APPL'.lower()),
- ('Django Software Foundation', 'No Tag'.lower()),
- ('Google', 'Do No Evil'.lower()),
- ('Yahoo', 'Internet Company'.lower())
+ qs,
+ [
+ ("Apple", "APPL".lower()),
+ ("Django Software Foundation", "No Tag".lower()),
+ ("Google", "Do No Evil".lower()),
+ ("Yahoo", "Internet Company".lower()),
],
- lambda c: (c.name, c.tagline_lower)
+ lambda c: (c.name, c.tagline_lower),
)
def test_boolean_value_annotation(self):
@@ -678,148 +914,214 @@ class NonAggregateAnnotationTestCase(TestCase):
qs = (
Publisher.objects.annotate(multiplier=Value(3))
# group by option => sum of value * multiplier
- .values('name')
- .annotate(multiplied_value_sum=Sum(F('multiplier') * F('num_awards')))
+ .values("name")
+ .annotate(multiplied_value_sum=Sum(F("multiplier") * F("num_awards")))
.order_by()
)
self.assertCountEqual(
- qs, [
- {'multiplied_value_sum': 9, 'name': 'Apress'},
- {'multiplied_value_sum': 0, 'name': "Jonno's House of Books"},
- {'multiplied_value_sum': 27, 'name': 'Morgan Kaufmann'},
- {'multiplied_value_sum': 21, 'name': 'Prentice Hall'},
- {'multiplied_value_sum': 3, 'name': 'Sams'},
- ]
+ qs,
+ [
+ {"multiplied_value_sum": 9, "name": "Apress"},
+ {"multiplied_value_sum": 0, "name": "Jonno's House of Books"},
+ {"multiplied_value_sum": 27, "name": "Morgan Kaufmann"},
+ {"multiplied_value_sum": 21, "name": "Prentice Hall"},
+ {"multiplied_value_sum": 3, "name": "Sams"},
+ ],
)
def test_arguments_must_be_expressions(self):
- msg = 'QuerySet.annotate() received non-expression(s): %s.'
+ msg = "QuerySet.annotate() received non-expression(s): %s."
with self.assertRaisesMessage(TypeError, msg % BooleanField()):
Book.objects.annotate(BooleanField())
with self.assertRaisesMessage(TypeError, msg % True):
Book.objects.annotate(is_book=True)
- with self.assertRaisesMessage(TypeError, msg % ', '.join([str(BooleanField()), 'True'])):
+ with self.assertRaisesMessage(
+ TypeError, msg % ", ".join([str(BooleanField()), "True"])
+ ):
Book.objects.annotate(BooleanField(), Value(False), is_book=True)
def test_chaining_annotation_filter_with_m2m(self):
- qs = Author.objects.filter(
- name='Adrian Holovaty',
- friends__age=35,
- ).annotate(
- jacob_name=F('friends__name'),
- ).filter(
- friends__age=29,
- ).annotate(
- james_name=F('friends__name'),
- ).values('jacob_name', 'james_name')
+ qs = (
+ Author.objects.filter(
+ name="Adrian Holovaty",
+ friends__age=35,
+ )
+ .annotate(
+ jacob_name=F("friends__name"),
+ )
+ .filter(
+ friends__age=29,
+ )
+ .annotate(
+ james_name=F("friends__name"),
+ )
+ .values("jacob_name", "james_name")
+ )
self.assertCountEqual(
qs,
- [{'jacob_name': 'Jacob Kaplan-Moss', 'james_name': 'James Bennett'}],
+ [{"jacob_name": "Jacob Kaplan-Moss", "james_name": "James Bennett"}],
)
def test_annotation_filter_with_subquery(self):
- long_books_qs = Book.objects.filter(
- publisher=OuterRef('pk'),
- pages__gt=400,
- ).values('publisher').annotate(count=Count('pk')).values('count')
- publisher_books_qs = Publisher.objects.annotate(
- total_books=Count('book'),
- ).filter(
- total_books=Subquery(long_books_qs, output_field=IntegerField()),
- ).values('name')
- self.assertCountEqual(publisher_books_qs, [{'name': 'Sams'}, {'name': 'Morgan Kaufmann'}])
+ long_books_qs = (
+ Book.objects.filter(
+ publisher=OuterRef("pk"),
+ pages__gt=400,
+ )
+ .values("publisher")
+ .annotate(count=Count("pk"))
+ .values("count")
+ )
+ publisher_books_qs = (
+ Publisher.objects.annotate(
+ total_books=Count("book"),
+ )
+ .filter(
+ total_books=Subquery(long_books_qs, output_field=IntegerField()),
+ )
+ .values("name")
+ )
+ self.assertCountEqual(
+ publisher_books_qs, [{"name": "Sams"}, {"name": "Morgan Kaufmann"}]
+ )
def test_annotation_exists_aggregate_values_chaining(self):
- qs = Book.objects.values('publisher').annotate(
- has_authors=Exists(Book.authors.through.objects.filter(book=OuterRef('pk'))),
- max_pubdate=Max('pubdate'),
- ).values_list('max_pubdate', flat=True).order_by('max_pubdate')
- self.assertCountEqual(qs, [
- datetime.date(1991, 10, 15),
- datetime.date(2008, 3, 3),
- datetime.date(2008, 6, 23),
- datetime.date(2008, 11, 3),
- ])
-
- @skipUnlessDBFeature('supports_subqueries_in_group_by')
+ qs = (
+ Book.objects.values("publisher")
+ .annotate(
+ has_authors=Exists(
+ Book.authors.through.objects.filter(book=OuterRef("pk"))
+ ),
+ max_pubdate=Max("pubdate"),
+ )
+ .values_list("max_pubdate", flat=True)
+ .order_by("max_pubdate")
+ )
+ self.assertCountEqual(
+ qs,
+ [
+ datetime.date(1991, 10, 15),
+ datetime.date(2008, 3, 3),
+ datetime.date(2008, 6, 23),
+ datetime.date(2008, 11, 3),
+ ],
+ )
+
+ @skipUnlessDBFeature("supports_subqueries_in_group_by")
def test_annotation_subquery_and_aggregate_values_chaining(self):
- qs = Book.objects.annotate(
- pub_year=ExtractYear('pubdate')
- ).values('pub_year').annotate(
- top_rating=Subquery(
- Book.objects.filter(
- pubdate__year=OuterRef('pub_year')
- ).order_by('-rating').values('rating')[:1]
- ),
- total_pages=Sum('pages'),
- ).values('pub_year', 'total_pages', 'top_rating')
- self.assertCountEqual(qs, [
- {'pub_year': 1991, 'top_rating': 5.0, 'total_pages': 946},
- {'pub_year': 1995, 'top_rating': 4.0, 'total_pages': 1132},
- {'pub_year': 2007, 'top_rating': 4.5, 'total_pages': 447},
- {'pub_year': 2008, 'top_rating': 4.0, 'total_pages': 1178},
- ])
+ qs = (
+ Book.objects.annotate(pub_year=ExtractYear("pubdate"))
+ .values("pub_year")
+ .annotate(
+ top_rating=Subquery(
+ Book.objects.filter(pubdate__year=OuterRef("pub_year"))
+ .order_by("-rating")
+ .values("rating")[:1]
+ ),
+ total_pages=Sum("pages"),
+ )
+ .values("pub_year", "total_pages", "top_rating")
+ )
+ self.assertCountEqual(
+ qs,
+ [
+ {"pub_year": 1991, "top_rating": 5.0, "total_pages": 946},
+ {"pub_year": 1995, "top_rating": 4.0, "total_pages": 1132},
+ {"pub_year": 2007, "top_rating": 4.5, "total_pages": 447},
+ {"pub_year": 2008, "top_rating": 4.0, "total_pages": 1178},
+ ],
+ )
def test_annotation_subquery_outerref_transform(self):
qs = Book.objects.annotate(
top_rating_year=Subquery(
- Book.objects.filter(
- pubdate__year=OuterRef('pubdate__year')
- ).order_by('-rating').values('rating')[:1]
+ Book.objects.filter(pubdate__year=OuterRef("pubdate__year"))
+ .order_by("-rating")
+ .values("rating")[:1]
),
- ).values('pubdate__year', 'top_rating_year')
- self.assertCountEqual(qs, [
- {'pubdate__year': 1991, 'top_rating_year': 5.0},
- {'pubdate__year': 1995, 'top_rating_year': 4.0},
- {'pubdate__year': 2007, 'top_rating_year': 4.5},
- {'pubdate__year': 2008, 'top_rating_year': 4.0},
- {'pubdate__year': 2008, 'top_rating_year': 4.0},
- {'pubdate__year': 2008, 'top_rating_year': 4.0},
- ])
+ ).values("pubdate__year", "top_rating_year")
+ self.assertCountEqual(
+ qs,
+ [
+ {"pubdate__year": 1991, "top_rating_year": 5.0},
+ {"pubdate__year": 1995, "top_rating_year": 4.0},
+ {"pubdate__year": 2007, "top_rating_year": 4.5},
+ {"pubdate__year": 2008, "top_rating_year": 4.0},
+ {"pubdate__year": 2008, "top_rating_year": 4.0},
+ {"pubdate__year": 2008, "top_rating_year": 4.0},
+ ],
+ )
def test_annotation_aggregate_with_m2o(self):
- qs = Author.objects.filter(age__lt=30).annotate(
- max_pages=Case(
- When(book_contact_set__isnull=True, then=Value(0)),
- default=Max(F('book__pages')),
- ),
- ).values('name', 'max_pages')
- self.assertCountEqual(qs, [
- {'name': 'James Bennett', 'max_pages': 300},
- {'name': 'Paul Bissex', 'max_pages': 0},
- {'name': 'Wesley J. Chun', 'max_pages': 0},
- ])
+ qs = (
+ Author.objects.filter(age__lt=30)
+ .annotate(
+ max_pages=Case(
+ When(book_contact_set__isnull=True, then=Value(0)),
+ default=Max(F("book__pages")),
+ ),
+ )
+ .values("name", "max_pages")
+ )
+ self.assertCountEqual(
+ qs,
+ [
+ {"name": "James Bennett", "max_pages": 300},
+ {"name": "Paul Bissex", "max_pages": 0},
+ {"name": "Wesley J. Chun", "max_pages": 0},
+ ],
+ )
class AliasTests(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='James Bennett', age=34)
- cls.a4 = Author.objects.create(name='Peter Norvig', age=57)
- cls.a5 = Author.objects.create(name='Stuart Russell', age=46)
- p1 = Publisher.objects.create(name='Apress', num_awards=3)
+ 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="James Bennett", age=34)
+ cls.a4 = Author.objects.create(name="Peter Norvig", age=57)
+ cls.a5 = Author.objects.create(name="Stuart Russell", age=46)
+ p1 = Publisher.objects.create(name="Apress", num_awards=3)
cls.b1 = Book.objects.create(
- isbn='159059725', pages=447, rating=4.5, price=Decimal('30.00'),
- contact=cls.a1, publisher=p1, pubdate=datetime.date(2007, 12, 6),
- name='The Definitive Guide to Django: Web Development Done Right',
+ isbn="159059725",
+ pages=447,
+ rating=4.5,
+ price=Decimal("30.00"),
+ contact=cls.a1,
+ publisher=p1,
+ pubdate=datetime.date(2007, 12, 6),
+ name="The Definitive Guide to Django: Web Development Done Right",
)
cls.b2 = Book.objects.create(
- isbn='159059996', pages=300, rating=4.0, price=Decimal('29.69'),
- contact=cls.a3, publisher=p1, pubdate=datetime.date(2008, 6, 23),
- name='Practical Django Projects',
+ isbn="159059996",
+ pages=300,
+ rating=4.0,
+ price=Decimal("29.69"),
+ contact=cls.a3,
+ publisher=p1,
+ pubdate=datetime.date(2008, 6, 23),
+ name="Practical Django Projects",
)
cls.b3 = Book.objects.create(
- isbn='013790395', pages=1132, rating=4.0, price=Decimal('82.80'),
- contact=cls.a4, publisher=p1, pubdate=datetime.date(1995, 1, 15),
- name='Artificial Intelligence: A Modern Approach',
+ isbn="013790395",
+ pages=1132,
+ rating=4.0,
+ price=Decimal("82.80"),
+ contact=cls.a4,
+ publisher=p1,
+ pubdate=datetime.date(1995, 1, 15),
+ name="Artificial Intelligence: A Modern Approach",
)
cls.b4 = Book.objects.create(
- isbn='155860191', pages=946, rating=5.0, price=Decimal('75.00'),
- contact=cls.a4, publisher=p1, pubdate=datetime.date(1991, 10, 15),
- name='Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp',
+ isbn="155860191",
+ pages=946,
+ rating=5.0,
+ price=Decimal("75.00"),
+ contact=cls.a4,
+ publisher=p1,
+ pubdate=datetime.date(1991, 10, 15),
+ name="Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp",
)
cls.b1.authors.add(cls.a1, cls.a2)
cls.b2.authors.add(cls.a3)
@@ -827,43 +1129,43 @@ class AliasTests(TestCase):
cls.b4.authors.add(cls.a4)
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),
)
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),
)
def test_basic_alias(self):
qs = Book.objects.alias(is_book=Value(1))
- self.assertIs(hasattr(qs.first(), 'is_book'), False)
+ self.assertIs(hasattr(qs.first(), "is_book"), False)
def test_basic_alias_annotation(self):
qs = Book.objects.alias(
is_book_alias=Value(1),
- ).annotate(is_book=F('is_book_alias'))
- self.assertIs(hasattr(qs.first(), 'is_book_alias'), False)
+ ).annotate(is_book=F("is_book_alias"))
+ self.assertIs(hasattr(qs.first(), "is_book_alias"), False)
for book in qs:
with self.subTest(book=book):
self.assertEqual(book.is_book, 1)
def test_basic_alias_f_annotation(self):
- qs = Book.objects.alias(
- another_rating_alias=F('rating')
- ).annotate(another_rating=F('another_rating_alias'))
- self.assertIs(hasattr(qs.first(), 'another_rating_alias'), False)
+ qs = Book.objects.alias(another_rating_alias=F("rating")).annotate(
+ another_rating=F("another_rating_alias")
+ )
+ self.assertIs(hasattr(qs.first(), "another_rating_alias"), False)
for book in qs:
with self.subTest(book=book):
self.assertEqual(book.another_rating, book.rating)
def test_basic_alias_f_transform_annotation(self):
qs = Book.objects.alias(
- pubdate_alias=F('pubdate'),
- ).annotate(pubdate_year=F('pubdate_alias__year'))
- self.assertIs(hasattr(qs.first(), 'pubdate_alias'), False)
+ pubdate_alias=F("pubdate"),
+ ).annotate(pubdate_year=F("pubdate_alias__year"))
+ self.assertIs(hasattr(qs.first(), "pubdate_alias"), False)
for book in qs:
with self.subTest(book=book):
self.assertEqual(book.pubdate_year, book.pubdate.year)
@@ -871,17 +1173,17 @@ class AliasTests(TestCase):
def test_alias_after_annotation(self):
qs = Book.objects.annotate(
is_book=Value(1),
- ).alias(is_book_alias=F('is_book'))
+ ).alias(is_book_alias=F("is_book"))
book = qs.first()
- self.assertIs(hasattr(book, 'is_book'), True)
- self.assertIs(hasattr(book, 'is_book_alias'), False)
+ self.assertIs(hasattr(book, "is_book"), True)
+ self.assertIs(hasattr(book, "is_book_alias"), False)
def test_overwrite_annotation_with_alias(self):
- qs = Book.objects.annotate(is_book=Value(1)).alias(is_book=F('is_book'))
- self.assertIs(hasattr(qs.first(), 'is_book'), False)
+ qs = Book.objects.annotate(is_book=Value(1)).alias(is_book=F("is_book"))
+ self.assertIs(hasattr(qs.first(), "is_book"), False)
def test_overwrite_alias_with_annotation(self):
- qs = Book.objects.alias(is_book=Value(1)).annotate(is_book=F('is_book'))
+ qs = Book.objects.alias(is_book=Value(1)).annotate(is_book=F("is_book"))
for book in qs:
with self.subTest(book=book):
self.assertEqual(book.is_book, 1)
@@ -889,24 +1191,28 @@ class AliasTests(TestCase):
def test_alias_annotation_expression(self):
qs = Book.objects.alias(
is_book_alias=Value(1),
- ).annotate(is_book=Coalesce('is_book_alias', 0))
- self.assertIs(hasattr(qs.first(), 'is_book_alias'), False)
+ ).annotate(is_book=Coalesce("is_book_alias", 0))
+ self.assertIs(hasattr(qs.first(), "is_book_alias"), False)
for book in qs:
with self.subTest(book=book):
self.assertEqual(book.is_book, 1)
def test_alias_default_alias_expression(self):
qs = Author.objects.alias(
- Sum('book__pages'),
+ Sum("book__pages"),
).filter(book__pages__sum__gt=2000)
- self.assertIs(hasattr(qs.first(), 'book__pages__sum'), False)
+ self.assertIs(hasattr(qs.first(), "book__pages__sum"), False)
self.assertSequenceEqual(qs, [self.a4])
def test_joined_alias_annotation(self):
- qs = Book.objects.select_related('publisher').alias(
- num_awards_alias=F('publisher__num_awards'),
- ).annotate(num_awards=F('num_awards_alias'))
- self.assertIs(hasattr(qs.first(), 'num_awards_alias'), False)
+ qs = (
+ Book.objects.select_related("publisher")
+ .alias(
+ num_awards_alias=F("publisher__num_awards"),
+ )
+ .annotate(num_awards=F("num_awards_alias"))
+ )
+ self.assertIs(hasattr(qs.first(), "num_awards_alias"), False)
for book in qs:
with self.subTest(book=book):
self.assertEqual(book.num_awards, book.publisher.num_awards)
@@ -914,14 +1220,14 @@ class AliasTests(TestCase):
def test_alias_annotate_with_aggregation(self):
qs = Book.objects.alias(
is_book_alias=Value(1),
- rating_count_alias=Count('rating'),
+ rating_count_alias=Count("rating"),
).annotate(
- is_book=F('is_book_alias'),
- rating_count=F('rating_count_alias'),
+ is_book=F("is_book_alias"),
+ rating_count=F("rating_count_alias"),
)
book = qs.first()
- self.assertIs(hasattr(book, 'is_book_alias'), False)
- self.assertIs(hasattr(book, 'rating_count_alias'), False)
+ self.assertIs(hasattr(book, "is_book_alias"), False)
+ self.assertIs(hasattr(book, "rating_count_alias"), False)
for book in qs:
with self.subTest(book=book):
self.assertEqual(book.is_book, 1)
@@ -929,61 +1235,71 @@ class AliasTests(TestCase):
def test_filter_alias_with_f(self):
qs = Book.objects.alias(
- other_rating=F('rating'),
+ other_rating=F("rating"),
).filter(other_rating=4.5)
- self.assertIs(hasattr(qs.first(), 'other_rating'), False)
+ self.assertIs(hasattr(qs.first(), "other_rating"), False)
self.assertSequenceEqual(qs, [self.b1])
def test_filter_alias_with_double_f(self):
qs = Book.objects.alias(
- other_rating=F('rating'),
- ).filter(other_rating=F('rating'))
- self.assertIs(hasattr(qs.first(), 'other_rating'), False)
+ other_rating=F("rating"),
+ ).filter(other_rating=F("rating"))
+ self.assertIs(hasattr(qs.first(), "other_rating"), False)
self.assertEqual(qs.count(), Book.objects.count())
def test_filter_alias_agg_with_double_f(self):
qs = Book.objects.alias(
- sum_rating=Sum('rating'),
- ).filter(sum_rating=F('sum_rating'))
- self.assertIs(hasattr(qs.first(), 'sum_rating'), False)
+ sum_rating=Sum("rating"),
+ ).filter(sum_rating=F("sum_rating"))
+ self.assertIs(hasattr(qs.first(), "sum_rating"), False)
self.assertEqual(qs.count(), Book.objects.count())
def test_update_with_alias(self):
Book.objects.alias(
- other_rating=F('rating') - 1,
- ).update(rating=F('other_rating'))
+ other_rating=F("rating") - 1,
+ ).update(rating=F("other_rating"))
self.b1.refresh_from_db()
self.assertEqual(self.b1.rating, 3.5)
def test_order_by_alias(self):
- qs = Author.objects.alias(other_age=F('age')).order_by('other_age')
- self.assertIs(hasattr(qs.first(), 'other_age'), False)
+ qs = Author.objects.alias(other_age=F("age")).order_by("other_age")
+ self.assertIs(hasattr(qs.first(), "other_age"), False)
self.assertQuerysetEqual(qs, [34, 34, 35, 46, 57], lambda a: a.age)
def test_order_by_alias_aggregate(self):
- qs = Author.objects.values('age').alias(age_count=Count('age')).order_by('age_count', 'age')
- self.assertIs(hasattr(qs.first(), 'age_count'), False)
- self.assertQuerysetEqual(qs, [35, 46, 57, 34], lambda a: a['age'])
+ qs = (
+ Author.objects.values("age")
+ .alias(age_count=Count("age"))
+ .order_by("age_count", "age")
+ )
+ self.assertIs(hasattr(qs.first(), "age_count"), False)
+ self.assertQuerysetEqual(qs, [35, 46, 57, 34], lambda a: a["age"])
def test_dates_alias(self):
qs = Book.objects.alias(
- pubdate_alias=F('pubdate'),
- ).dates('pubdate_alias', 'month')
- self.assertCountEqual(qs, [
- datetime.date(1991, 10, 1),
- datetime.date(1995, 1, 1),
- datetime.date(2007, 12, 1),
- datetime.date(2008, 6, 1),
- ])
+ pubdate_alias=F("pubdate"),
+ ).dates("pubdate_alias", "month")
+ self.assertCountEqual(
+ qs,
+ [
+ datetime.date(1991, 10, 1),
+ datetime.date(1995, 1, 1),
+ datetime.date(2007, 12, 1),
+ datetime.date(2008, 6, 1),
+ ],
+ )
def test_datetimes_alias(self):
qs = Store.objects.alias(
- original_opening_alias=F('original_opening'),
- ).datetimes('original_opening_alias', 'year')
- self.assertCountEqual(qs, [
- datetime.datetime(1994, 1, 1),
- datetime.datetime(2001, 1, 1),
- ])
+ original_opening_alias=F("original_opening"),
+ ).datetimes("original_opening_alias", "year")
+ self.assertCountEqual(
+ qs,
+ [
+ datetime.datetime(1994, 1, 1),
+ datetime.datetime(2001, 1, 1),
+ ],
+ )
def test_aggregate_alias(self):
msg = (
@@ -991,28 +1307,28 @@ class AliasTests(TestCase):
)
with self.assertRaisesMessage(FieldError, msg):
Author.objects.alias(
- other_age=F('age'),
- ).aggregate(otherage_sum=Sum('other_age'))
+ other_age=F("age"),
+ ).aggregate(otherage_sum=Sum("other_age"))
def test_defer_only_alias(self):
- qs = Book.objects.alias(rating_alias=F('rating') - 1)
+ qs = Book.objects.alias(rating_alias=F("rating") - 1)
msg = "Book has no field named 'rating_alias'"
- for operation in ['defer', 'only']:
+ for operation in ["defer", "only"]:
with self.subTest(operation=operation):
with self.assertRaisesMessage(FieldDoesNotExist, msg):
- getattr(qs, operation)('rating_alias').first()
+ getattr(qs, operation)("rating_alias").first()
- @skipUnlessDBFeature('can_distinct_on_fields')
+ @skipUnlessDBFeature("can_distinct_on_fields")
def test_distinct_on_alias(self):
- qs = Book.objects.alias(rating_alias=F('rating') - 1)
+ qs = Book.objects.alias(rating_alias=F("rating") - 1)
msg = "Cannot resolve keyword 'rating_alias' into field."
with self.assertRaisesMessage(FieldError, msg):
- qs.distinct('rating_alias').first()
+ qs.distinct("rating_alias").first()
def test_values_alias(self):
- qs = Book.objects.alias(rating_alias=F('rating') - 1)
+ qs = Book.objects.alias(rating_alias=F("rating") - 1)
msg = "Cannot select the 'rating_alias' alias. Use annotate() to promote it."
- for operation in ['values', 'values_list']:
+ for operation in ["values", "values_list"]:
with self.subTest(operation=operation):
with self.assertRaisesMessage(FieldError, msg):
- getattr(qs, operation)('rating_alias')
+ getattr(qs, operation)("rating_alias")