浏览代码

Fixed #32906 -- Added docs and tests for using key and index lookups on JSONBAgg results.

Co-authored-by: Mariusz Felisiak <felisiak.mariusz@gmail.com>
abhiabhi94 3 年之前
父节点
当前提交
5a634a7b6f
共有 2 个文件被更改,包括 69 次插入2 次删除
  1. 25 1
      docs/ref/contrib/postgres/aggregates.txt
  2. 44 1
      tests/postgres_tests/test_aggregates.py

+ 25 - 1
docs/ref/contrib/postgres/aggregates.txt

@@ -125,7 +125,8 @@ General-purpose aggregation functions
 .. class:: JSONBAgg(expressions, distinct=False, filter=None, default=None, ordering=(), **extra)
 
     Returns the input values as a ``JSON`` array, or ``default`` if there are
-    no values.
+    no values. You can query the result using :lookup:`key and index lookups
+    <jsonfield.key>`.
 
     .. attribute:: distinct
 
@@ -145,6 +146,29 @@ General-purpose aggregation functions
 
         Examples are the same as for :attr:`ArrayAgg.ordering`.
 
+    Usage example::
+
+        class Room(models.Model):
+            number = models.IntegerField(unique=True)
+
+        class HotelReservation(model.Model):
+            room = models.ForeignKey('Room', on_delete=models.CASCADE)
+            start = models.DateTimeField()
+            end = models.DateTimeField()
+            requirements = models.JSONField(blank=True, null=True)
+
+        >>> from django.contrib.postgres.aggregates import JSONBAgg
+        >>> Room.objects.annotate(
+        ...     requirements=JSONBAgg(
+        ...         'hotelreservation__requirements',
+        ...         ordering='-hotelreservation__start',
+        ...     )
+        ... ).filter(requirements__0__sea_view=True).values('number', 'requirements')
+        <QuerySet [{'number': 102, 'requirements': [
+            {'parking': False, 'sea_view': True, 'double_bed': False},
+            {'parking': True, 'double_bed': True}
+        ]}]>
+
     .. deprecated:: 4.0
 
         If there are no rows and ``default`` is not provided, ``JSONBAgg``

+ 44 - 1
tests/postgres_tests/test_aggregates.py

@@ -4,10 +4,11 @@ from django.db.models import (
 from django.db.models.fields.json import KeyTextTransform, KeyTransform
 from django.db.models.functions import Cast, Concat, Substr
 from django.test.utils import Approximate, ignore_warnings
+from django.utils import timezone
 from django.utils.deprecation import RemovedInDjango50Warning
 
 from . import PostgreSQLTestCase
-from .models import AggregateTestModel, StatTestModel
+from .models import AggregateTestModel, HotelReservation, Room, StatTestModel
 
 try:
     from django.contrib.postgres.aggregates import (
@@ -392,6 +393,48 @@ class TestGeneralAggregate(PostgreSQLTestCase):
         )
         self.assertEqual(values, {'jsonbagg': ['en', 'pl']})
 
+    def test_jsonb_agg_key_index_transforms(self):
+        room101 = Room.objects.create(number=101)
+        room102 = Room.objects.create(number=102)
+        datetimes = [
+            timezone.datetime(2018, 6, 20),
+            timezone.datetime(2018, 6, 24),
+            timezone.datetime(2018, 6, 28),
+        ]
+        HotelReservation.objects.create(
+            datespan=(datetimes[0].date(), datetimes[1].date()),
+            start=datetimes[0],
+            end=datetimes[1],
+            room=room102,
+            requirements={'double_bed': True, 'parking': True},
+        )
+        HotelReservation.objects.create(
+            datespan=(datetimes[1].date(), datetimes[2].date()),
+            start=datetimes[1],
+            end=datetimes[2],
+            room=room102,
+            requirements={'double_bed': False, 'sea_view': True, 'parking': False},
+        )
+        HotelReservation.objects.create(
+            datespan=(datetimes[0].date(), datetimes[2].date()),
+            start=datetimes[0],
+            end=datetimes[2],
+            room=room101,
+            requirements={'sea_view': False},
+        )
+        values = Room.objects.annotate(
+            requirements=JSONBAgg(
+                'hotelreservation__requirements',
+                ordering='-hotelreservation__start',
+            )
+        ).filter(requirements__0__sea_view=True).values('number', 'requirements')
+        self.assertSequenceEqual(values, [
+            {'number': 102, 'requirements': [
+                {'double_bed': False, 'sea_view': True, 'parking': False},
+                {'double_bed': True, 'parking': True},
+            ]},
+        ])
+
     def test_string_agg_array_agg_ordering_in_subquery(self):
         stats = []
         for i, agg in enumerate(AggregateTestModel.objects.order_by('char_field')):