|  | 
|  | 1 | +import operator | 
|  | 2 | + | 
| 1 | 3 | from django.core.exceptions import ValidationError | 
| 2 | 4 | from django.db import models | 
|  | 5 | +from django.db.models import ExpressionWrapper, F, Max, Sum | 
| 3 | 6 | from django.test import SimpleTestCase, TestCase | 
| 4 | 7 | from django.test.utils import isolate_apps | 
| 5 | 8 | 
 | 
| @@ -100,6 +103,43 @@ def test_order_by_embedded_field(self): | 
| 100 | 103 |  qs = Holder.objects.filter(data__integer__gt=3).order_by("-data__integer") | 
| 101 | 104 |  self.assertSequenceEqual(qs, list(reversed(self.objs[4:]))) | 
| 102 | 105 | 
 | 
|  | 106 | + def test_order_and_group_by_embedded_field(self): | 
|  | 107 | + # Create and sort test data by `data__integer`. | 
|  | 108 | + expected_objs = sorted( | 
|  | 109 | + (Holder.objects.create(data=Data(integer=x)) for x in range(6)), | 
|  | 110 | + key=lambda x: x.data.integer, | 
|  | 111 | + ) | 
|  | 112 | + # Group by `data__integer + 5` and get the latest `data__auto_now` | 
|  | 113 | + # datetime. | 
|  | 114 | + qs = ( | 
|  | 115 | + Holder.objects.annotate( | 
|  | 116 | + group=ExpressionWrapper(F("data__integer") + 5, output_field=models.IntegerField()), | 
|  | 117 | + ) | 
|  | 118 | + .values("group") | 
|  | 119 | + .annotate(max_auto_now=Max("data__auto_now")) | 
|  | 120 | + .order_by("data__integer") | 
|  | 121 | + ) | 
|  | 122 | + # Each unique `data__integer` is correctly grouped and annotated. | 
|  | 123 | + self.assertSequenceEqual( | 
|  | 124 | + [{**e, "max_auto_now": e["max_auto_now"]} for e in qs], | 
|  | 125 | + [ | 
|  | 126 | + {"group": e.data.integer + 5, "max_auto_now": truncate_ms(e.data.auto_now)} | 
|  | 127 | + for e in expected_objs | 
|  | 128 | + ], | 
|  | 129 | + ) | 
|  | 130 | + | 
|  | 131 | + def test_order_and_group_by_embedded_field_annotation(self): | 
|  | 132 | + # Create repeated `data__integer` values. | 
|  | 133 | + [Holder.objects.create(data=Data(integer=x)) for x in range(6)] | 
|  | 134 | + # Group by `data__integer` and compute the sum of occurrences. | 
|  | 135 | + qs = ( | 
|  | 136 | + Holder.objects.values("data__integer") | 
|  | 137 | + .annotate(sum=Sum("data__integer")) | 
|  | 138 | + .order_by("sum") | 
|  | 139 | + ) | 
|  | 140 | + # The sum is twice the integer values since each appears twice. | 
|  | 141 | + self.assertQuerySetEqual(qs, [0, 2, 4, 6, 8, 10], operator.itemgetter("sum")) | 
|  | 142 | + | 
| 103 | 143 |  def test_nested(self): | 
| 104 | 144 |  obj = Book.objects.create( | 
| 105 | 145 |  author=Author(name="Shakespeare", age=55, address=Address(city="NYC", state="NY")) | 
|  | 
0 commit comments