diff --git a/django/contrib/postgres/aggregates/general.py b/django/contrib/postgres/aggregates/general.py index 00957bdab0..f78eed7a60 100644 --- a/django/contrib/postgres/aggregates/general.py +++ b/django/contrib/postgres/aggregates/general.py @@ -1,5 +1,5 @@ from django.contrib.postgres.fields import ArrayField -from django.db.models import Aggregate, JSONField, Value +from django.db.models import Aggregate, BooleanField, JSONField, Value from .mixins import OrderableAggMixin @@ -33,10 +33,12 @@ class BitOr(Aggregate): class BoolAnd(Aggregate): function = 'BOOL_AND' + output_field = BooleanField() class BoolOr(Aggregate): function = 'BOOL_OR' + output_field = BooleanField() class JSONBAgg(OrderableAggMixin, Aggregate): diff --git a/docs/ref/contrib/postgres/aggregates.txt b/docs/ref/contrib/postgres/aggregates.txt index aad64a4db7..31202d5304 100644 --- a/docs/ref/contrib/postgres/aggregates.txt +++ b/docs/ref/contrib/postgres/aggregates.txt @@ -82,11 +82,11 @@ General-purpose aggregation functions published = models.BooleanField() rank = models.IntegerField() - >>> from django.db.models import BooleanField, Q + >>> from django.db.models import Q >>> from django.contrib.postgres.aggregates import BoolAnd >>> Comment.objects.aggregate(booland=BoolAnd('published')) {'booland': False} - >>> Comment.objects.aggregate(booland=BoolAnd(Q(rank__lt=100), output_field=BooleanField())) + >>> Comment.objects.aggregate(booland=BoolAnd(Q(rank__lt=100))) {'booland': True} ``BoolOr`` @@ -104,11 +104,11 @@ General-purpose aggregation functions published = models.BooleanField() rank = models.IntegerField() - >>> from django.db.models import BooleanField, Q + >>> from django.db.models import Q >>> from django.contrib.postgres.aggregates import BoolOr >>> Comment.objects.aggregate(boolor=BoolOr('published')) {'boolor': True} - >>> Comment.objects.aggregate(boolor=BoolOr(Q(rank__gt=2), output_field=BooleanField())) + >>> Comment.objects.aggregate(boolor=BoolOr(Q(rank__gt=2))) {'boolor': False} ``JSONBAgg`` diff --git a/tests/postgres_tests/test_aggregates.py b/tests/postgres_tests/test_aggregates.py index 8f3d8bb6ef..19f9596576 100644 --- a/tests/postgres_tests/test_aggregates.py +++ b/tests/postgres_tests/test_aggregates.py @@ -155,6 +155,12 @@ class TestGeneralAggregate(PostgreSQLTestCase): values = AggregateTestModel.objects.aggregate(booland=BoolAnd('boolean_field')) self.assertEqual(values, {'booland': None}) + def test_bool_and_q_object(self): + values = AggregateTestModel.objects.aggregate( + booland=BoolAnd(Q(integer_field__gt=2)), + ) + self.assertEqual(values, {'booland': False}) + def test_bool_or_general(self): values = AggregateTestModel.objects.aggregate(boolor=BoolOr('boolean_field')) self.assertEqual(values, {'boolor': True}) @@ -164,6 +170,12 @@ class TestGeneralAggregate(PostgreSQLTestCase): values = AggregateTestModel.objects.aggregate(boolor=BoolOr('boolean_field')) self.assertEqual(values, {'boolor': None}) + def test_bool_or_q_object(self): + values = AggregateTestModel.objects.aggregate( + boolor=BoolOr(Q(integer_field__gt=2)), + ) + self.assertEqual(values, {'boolor': False}) + def test_string_agg_requires_delimiter(self): with self.assertRaises(TypeError): AggregateTestModel.objects.aggregate(stringagg=StringAgg('char_field'))