mirror of
https://github.com/django/django.git
synced 2025-06-07 04:29:12 +00:00
Refs #35444 -- Deprecated contrib.postgres aggregates ordering for order_by.
Aligns the argument with SQL standards already used in Window.order_by and sets up for adding support to Aggregate.
This commit is contained in:
parent
46b3e7dd8c
commit
d734f1651c
@ -17,7 +17,7 @@ __all__ = [
|
|||||||
|
|
||||||
class ArrayAgg(OrderableAggMixin, Aggregate):
|
class ArrayAgg(OrderableAggMixin, Aggregate):
|
||||||
function = "ARRAY_AGG"
|
function = "ARRAY_AGG"
|
||||||
template = "%(function)s(%(distinct)s%(expressions)s %(ordering)s)"
|
template = "%(function)s(%(distinct)s%(expressions)s %(order_by)s)"
|
||||||
allow_distinct = True
|
allow_distinct = True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -49,14 +49,14 @@ class BoolOr(Aggregate):
|
|||||||
|
|
||||||
class JSONBAgg(OrderableAggMixin, Aggregate):
|
class JSONBAgg(OrderableAggMixin, Aggregate):
|
||||||
function = "JSONB_AGG"
|
function = "JSONB_AGG"
|
||||||
template = "%(function)s(%(distinct)s%(expressions)s %(ordering)s)"
|
template = "%(function)s(%(distinct)s%(expressions)s %(order_by)s)"
|
||||||
allow_distinct = True
|
allow_distinct = True
|
||||||
output_field = JSONField()
|
output_field = JSONField()
|
||||||
|
|
||||||
|
|
||||||
class StringAgg(OrderableAggMixin, Aggregate):
|
class StringAgg(OrderableAggMixin, Aggregate):
|
||||||
function = "STRING_AGG"
|
function = "STRING_AGG"
|
||||||
template = "%(function)s(%(distinct)s%(expressions)s %(ordering)s)"
|
template = "%(function)s(%(distinct)s%(expressions)s %(order_by)s)"
|
||||||
allow_distinct = True
|
allow_distinct = True
|
||||||
output_field = TextField()
|
output_field = TextField()
|
||||||
|
|
||||||
|
@ -1,15 +1,30 @@
|
|||||||
|
import warnings
|
||||||
|
|
||||||
from django.core.exceptions import FullResultSet
|
from django.core.exceptions import FullResultSet
|
||||||
from django.db.models.expressions import OrderByList
|
from django.db.models.expressions import OrderByList
|
||||||
|
from django.utils.deprecation import RemovedInDjango61Warning
|
||||||
|
|
||||||
|
|
||||||
class OrderableAggMixin:
|
class OrderableAggMixin:
|
||||||
def __init__(self, *expressions, ordering=(), **extra):
|
# RemovedInDjango61Warning: When the deprecation ends, replace with:
|
||||||
if not ordering:
|
# def __init__(self, *expressions, order_by=(), **extra):
|
||||||
|
def __init__(self, *expressions, ordering=(), order_by=(), **extra):
|
||||||
|
# RemovedInDjango61Warning.
|
||||||
|
if ordering:
|
||||||
|
warnings.warn(
|
||||||
|
"The ordering argument is deprecated. Use order_by instead.",
|
||||||
|
category=RemovedInDjango61Warning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
if order_by:
|
||||||
|
raise TypeError("Cannot specify both order_by and ordering.")
|
||||||
|
order_by = ordering
|
||||||
|
if not order_by:
|
||||||
self.order_by = None
|
self.order_by = None
|
||||||
elif isinstance(ordering, (list, tuple)):
|
elif isinstance(order_by, (list, tuple)):
|
||||||
self.order_by = OrderByList(*ordering)
|
self.order_by = OrderByList(*order_by)
|
||||||
else:
|
else:
|
||||||
self.order_by = OrderByList(ordering)
|
self.order_by = OrderByList(order_by)
|
||||||
super().__init__(*expressions, **extra)
|
super().__init__(*expressions, **extra)
|
||||||
|
|
||||||
def resolve_expression(self, *args, **kwargs):
|
def resolve_expression(self, *args, **kwargs):
|
||||||
@ -25,12 +40,12 @@ class OrderableAggMixin:
|
|||||||
return super().set_source_expressions(exprs)
|
return super().set_source_expressions(exprs)
|
||||||
|
|
||||||
def as_sql(self, compiler, connection):
|
def as_sql(self, compiler, connection):
|
||||||
*source_exprs, filtering_expr, ordering_expr = self.get_source_expressions()
|
*source_exprs, filtering_expr, order_by_expr = self.get_source_expressions()
|
||||||
|
|
||||||
order_by_sql = ""
|
order_by_sql = ""
|
||||||
order_by_params = []
|
order_by_params = []
|
||||||
if ordering_expr is not None:
|
if order_by_expr is not None:
|
||||||
order_by_sql, order_by_params = compiler.compile(ordering_expr)
|
order_by_sql, order_by_params = compiler.compile(order_by_expr)
|
||||||
|
|
||||||
filter_params = []
|
filter_params = []
|
||||||
if filtering_expr is not None:
|
if filtering_expr is not None:
|
||||||
@ -43,5 +58,5 @@ class OrderableAggMixin:
|
|||||||
for source_expr in source_exprs:
|
for source_expr in source_exprs:
|
||||||
source_params += compiler.compile(source_expr)[1]
|
source_params += compiler.compile(source_expr)[1]
|
||||||
|
|
||||||
sql, _ = super().as_sql(compiler, connection, ordering=order_by_sql)
|
sql, _ = super().as_sql(compiler, connection, order_by=order_by_sql)
|
||||||
return sql, (*source_params, *order_by_params, *filter_params)
|
return sql, (*source_params, *order_by_params, *filter_params)
|
||||||
|
@ -22,6 +22,11 @@ details on these changes.
|
|||||||
``django.contrib.auth.login()`` and ``django.contrib.auth.alogin()`` will be
|
``django.contrib.auth.login()`` and ``django.contrib.auth.alogin()`` will be
|
||||||
removed.
|
removed.
|
||||||
|
|
||||||
|
* The ``ordering`` keyword argument of the PostgreSQL specific aggregation
|
||||||
|
functions ``django.contrib.postgres.aggregates.ArrayAgg``,
|
||||||
|
``django.contrib.postgres.aggregates.JSONBAgg``, and
|
||||||
|
``django.contrib.postgres.aggregates.StringAgg`` will be removed.
|
||||||
|
|
||||||
.. _deprecation-removed-in-6.0:
|
.. _deprecation-removed-in-6.0:
|
||||||
|
|
||||||
6.0
|
6.0
|
||||||
|
@ -30,7 +30,7 @@ General-purpose aggregation functions
|
|||||||
``ArrayAgg``
|
``ArrayAgg``
|
||||||
------------
|
------------
|
||||||
|
|
||||||
.. class:: ArrayAgg(expression, distinct=False, filter=None, default=None, ordering=(), **extra)
|
.. class:: ArrayAgg(expression, distinct=False, filter=None, default=None, order_by=(), **extra)
|
||||||
|
|
||||||
Returns a list of values, including nulls, concatenated into an array, or
|
Returns a list of values, including nulls, concatenated into an array, or
|
||||||
``default`` if there are no values.
|
``default`` if there are no values.
|
||||||
@ -40,7 +40,9 @@ General-purpose aggregation functions
|
|||||||
An optional boolean argument that determines if array values
|
An optional boolean argument that determines if array values
|
||||||
will be distinct. Defaults to ``False``.
|
will be distinct. Defaults to ``False``.
|
||||||
|
|
||||||
.. attribute:: ordering
|
.. attribute:: order_by
|
||||||
|
|
||||||
|
.. versionadded:: 5.2
|
||||||
|
|
||||||
An optional string of a field name (with an optional ``"-"`` prefix
|
An optional string of a field name (with an optional ``"-"`` prefix
|
||||||
which indicates descending order) or an expression (or a tuple or list
|
which indicates descending order) or an expression (or a tuple or list
|
||||||
@ -55,6 +57,11 @@ General-purpose aggregation functions
|
|||||||
|
|
||||||
F("some_field").desc()
|
F("some_field").desc()
|
||||||
|
|
||||||
|
.. deprecated:: 5.2
|
||||||
|
|
||||||
|
The ``ordering`` keyword argument is deprecated. Use
|
||||||
|
:attr:`ArrayAgg.order_by` instead.
|
||||||
|
|
||||||
``BitAnd``
|
``BitAnd``
|
||||||
----------
|
----------
|
||||||
|
|
||||||
@ -130,7 +137,7 @@ General-purpose aggregation functions
|
|||||||
``JSONBAgg``
|
``JSONBAgg``
|
||||||
------------
|
------------
|
||||||
|
|
||||||
.. class:: JSONBAgg(expressions, distinct=False, filter=None, default=None, ordering=(), **extra)
|
.. class:: JSONBAgg(expressions, distinct=False, filter=None, default=None, order_by=(), **extra)
|
||||||
|
|
||||||
Returns the input values as a ``JSON`` array, or ``default`` if there are
|
Returns the input values as a ``JSON`` array, or ``default`` if there are
|
||||||
no values. You can query the result using :lookup:`key and index lookups
|
no values. You can query the result using :lookup:`key and index lookups
|
||||||
@ -141,14 +148,16 @@ General-purpose aggregation functions
|
|||||||
An optional boolean argument that determines if array values will be
|
An optional boolean argument that determines if array values will be
|
||||||
distinct. Defaults to ``False``.
|
distinct. Defaults to ``False``.
|
||||||
|
|
||||||
.. attribute:: ordering
|
.. attribute:: order_by
|
||||||
|
|
||||||
|
.. versionadded:: 5.2
|
||||||
|
|
||||||
An optional string of a field name (with an optional ``"-"`` prefix
|
An optional string of a field name (with an optional ``"-"`` prefix
|
||||||
which indicates descending order) or an expression (or a tuple or list
|
which indicates descending order) or an expression (or a tuple or list
|
||||||
of strings and/or expressions) that specifies the ordering of the
|
of strings and/or expressions) that specifies the ordering of the
|
||||||
elements in the result list.
|
elements in the result list.
|
||||||
|
|
||||||
Examples are the same as for :attr:`ArrayAgg.ordering`.
|
Examples are the same as for :attr:`ArrayAgg.order_by`.
|
||||||
|
|
||||||
Usage example::
|
Usage example::
|
||||||
|
|
||||||
@ -168,7 +177,7 @@ General-purpose aggregation functions
|
|||||||
>>> Room.objects.annotate(
|
>>> Room.objects.annotate(
|
||||||
... requirements=JSONBAgg(
|
... requirements=JSONBAgg(
|
||||||
... "hotelreservation__requirements",
|
... "hotelreservation__requirements",
|
||||||
... ordering="-hotelreservation__start",
|
... order_by="-hotelreservation__start",
|
||||||
... )
|
... )
|
||||||
... ).filter(requirements__0__sea_view=True).values("number", "requirements")
|
... ).filter(requirements__0__sea_view=True).values("number", "requirements")
|
||||||
<QuerySet [{'number': 102, 'requirements': [
|
<QuerySet [{'number': 102, 'requirements': [
|
||||||
@ -176,10 +185,15 @@ General-purpose aggregation functions
|
|||||||
{'parking': True, 'double_bed': True}
|
{'parking': True, 'double_bed': True}
|
||||||
]}]>
|
]}]>
|
||||||
|
|
||||||
|
.. deprecated:: 5.2
|
||||||
|
|
||||||
|
The ``ordering`` keyword argument is deprecated. Use
|
||||||
|
:attr:`JSONBAgg.order_by` instead.
|
||||||
|
|
||||||
``StringAgg``
|
``StringAgg``
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
.. class:: StringAgg(expression, delimiter, distinct=False, filter=None, default=None, ordering=())
|
.. class:: StringAgg(expression, delimiter, distinct=False, filter=None, default=None, order_by=())
|
||||||
|
|
||||||
Returns the input values concatenated into a string, separated by
|
Returns the input values concatenated into a string, separated by
|
||||||
the ``delimiter`` string, or ``default`` if there are no values.
|
the ``delimiter`` string, or ``default`` if there are no values.
|
||||||
@ -193,14 +207,16 @@ General-purpose aggregation functions
|
|||||||
An optional boolean argument that determines if concatenated values
|
An optional boolean argument that determines if concatenated values
|
||||||
will be distinct. Defaults to ``False``.
|
will be distinct. Defaults to ``False``.
|
||||||
|
|
||||||
.. attribute:: ordering
|
.. attribute:: order_by
|
||||||
|
|
||||||
|
.. versionadded:: 5.2
|
||||||
|
|
||||||
An optional string of a field name (with an optional ``"-"`` prefix
|
An optional string of a field name (with an optional ``"-"`` prefix
|
||||||
which indicates descending order) or an expression (or a tuple or list
|
which indicates descending order) or an expression (or a tuple or list
|
||||||
of strings and/or expressions) that specifies the ordering of the
|
of strings and/or expressions) that specifies the ordering of the
|
||||||
elements in the result string.
|
elements in the result string.
|
||||||
|
|
||||||
Examples are the same as for :attr:`ArrayAgg.ordering`.
|
Examples are the same as for :attr:`ArrayAgg.order_by`.
|
||||||
|
|
||||||
Usage example::
|
Usage example::
|
||||||
|
|
||||||
@ -224,13 +240,18 @@ General-purpose aggregation functions
|
|||||||
... publication_names=StringAgg(
|
... publication_names=StringAgg(
|
||||||
... "publications__title",
|
... "publications__title",
|
||||||
... delimiter=", ",
|
... delimiter=", ",
|
||||||
... ordering="publications__title",
|
... order_by="publications__title",
|
||||||
... )
|
... )
|
||||||
... ).values("headline", "publication_names")
|
... ).values("headline", "publication_names")
|
||||||
<QuerySet [{
|
<QuerySet [{
|
||||||
'headline': 'NASA uses Python', 'publication_names': 'Science News, The Python Journal'
|
'headline': 'NASA uses Python', 'publication_names': 'Science News, The Python Journal'
|
||||||
}]>
|
}]>
|
||||||
|
|
||||||
|
.. deprecated:: 5.2
|
||||||
|
|
||||||
|
The ``ordering`` keyword argument is deprecated. Use
|
||||||
|
:attr:`StringAgg.order_by` instead.
|
||||||
|
|
||||||
Aggregate functions for statistics
|
Aggregate functions for statistics
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
|
@ -250,7 +250,7 @@ Minor features
|
|||||||
* The new ``ExclusionConstraint.opclasses`` attribute allows setting PostgreSQL
|
* The new ``ExclusionConstraint.opclasses`` attribute allows setting PostgreSQL
|
||||||
operator classes.
|
operator classes.
|
||||||
|
|
||||||
* The new :attr:`.JSONBAgg.ordering` attribute determines the ordering of the
|
* The new ``JSONBAgg.ordering`` attribute determines the ordering of the
|
||||||
aggregated elements.
|
aggregated elements.
|
||||||
|
|
||||||
* The new :attr:`.JSONBAgg.distinct` attribute determines if aggregated values
|
* The new :attr:`.JSONBAgg.distinct` attribute determines if aggregated values
|
||||||
|
@ -495,3 +495,9 @@ Miscellaneous
|
|||||||
* The fallback to ``request.user`` when ``user`` is ``None`` in
|
* The fallback to ``request.user`` when ``user`` is ``None`` in
|
||||||
``django.contrib.auth.login()`` and ``django.contrib.auth.alogin()`` will be
|
``django.contrib.auth.login()`` and ``django.contrib.auth.alogin()`` will be
|
||||||
removed.
|
removed.
|
||||||
|
|
||||||
|
* The ``ordering`` keyword argument of the PostgreSQL specific aggregation
|
||||||
|
functions ``django.contrib.postgres.aggregates.ArrayAgg``,
|
||||||
|
``django.contrib.postgres.aggregates.JSONBAgg``, and
|
||||||
|
``django.contrib.postgres.aggregates.StringAgg`` is deprecated in favor
|
||||||
|
of the ``order_by`` argument.
|
||||||
|
@ -15,6 +15,7 @@ from django.db.models.fields.json import KeyTextTransform, KeyTransform
|
|||||||
from django.db.models.functions import Cast, Concat, LPad, Substr
|
from django.db.models.functions import Cast, Concat, LPad, Substr
|
||||||
from django.test.utils import Approximate
|
from django.test.utils import Approximate
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.utils.deprecation import RemovedInDjango61Warning
|
||||||
|
|
||||||
from . import PostgreSQLTestCase
|
from . import PostgreSQLTestCase
|
||||||
from .models import AggregateTestModel, HotelReservation, Room, StatTestModel
|
from .models import AggregateTestModel, HotelReservation, Room, StatTestModel
|
||||||
@ -148,12 +149,36 @@ class TestGeneralAggregate(PostgreSQLTestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(values, {"aggregation": expected_result})
|
self.assertEqual(values, {"aggregation": expected_result})
|
||||||
|
|
||||||
|
def test_ordering_warns_of_deprecation(self):
|
||||||
|
msg = "The ordering argument is deprecated. Use order_by instead."
|
||||||
|
with self.assertWarnsMessage(RemovedInDjango61Warning, msg) as ctx:
|
||||||
|
values = AggregateTestModel.objects.aggregate(
|
||||||
|
arrayagg=ArrayAgg("integer_field", ordering=F("integer_field").desc())
|
||||||
|
)
|
||||||
|
self.assertEqual(values, {"arrayagg": [2, 1, 0, 0]})
|
||||||
|
self.assertEqual(ctx.filename, __file__)
|
||||||
|
|
||||||
|
def test_ordering_and_order_by_causes_error(self):
|
||||||
|
with self.assertWarns(RemovedInDjango61Warning):
|
||||||
|
with self.assertRaisesMessage(
|
||||||
|
TypeError,
|
||||||
|
"Cannot specify both order_by and ordering.",
|
||||||
|
):
|
||||||
|
AggregateTestModel.objects.aggregate(
|
||||||
|
stringagg=StringAgg(
|
||||||
|
"char_field",
|
||||||
|
delimiter=Value("'"),
|
||||||
|
order_by="char_field",
|
||||||
|
ordering="char_field",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def test_array_agg_charfield(self):
|
def test_array_agg_charfield(self):
|
||||||
values = AggregateTestModel.objects.aggregate(arrayagg=ArrayAgg("char_field"))
|
values = AggregateTestModel.objects.aggregate(arrayagg=ArrayAgg("char_field"))
|
||||||
self.assertEqual(values, {"arrayagg": ["Foo1", "Foo2", "Foo4", "Foo3"]})
|
self.assertEqual(values, {"arrayagg": ["Foo1", "Foo2", "Foo4", "Foo3"]})
|
||||||
|
|
||||||
def test_array_agg_charfield_ordering(self):
|
def test_array_agg_charfield_order_by(self):
|
||||||
ordering_test_cases = (
|
order_by_test_cases = (
|
||||||
(F("char_field").desc(), ["Foo4", "Foo3", "Foo2", "Foo1"]),
|
(F("char_field").desc(), ["Foo4", "Foo3", "Foo2", "Foo1"]),
|
||||||
(F("char_field").asc(), ["Foo1", "Foo2", "Foo3", "Foo4"]),
|
(F("char_field").asc(), ["Foo1", "Foo2", "Foo3", "Foo4"]),
|
||||||
(F("char_field"), ["Foo1", "Foo2", "Foo3", "Foo4"]),
|
(F("char_field"), ["Foo1", "Foo2", "Foo3", "Foo4"]),
|
||||||
@ -178,10 +203,10 @@ class TestGeneralAggregate(PostgreSQLTestCase):
|
|||||||
["Foo3", "Foo1", "Foo2", "Foo4"],
|
["Foo3", "Foo1", "Foo2", "Foo4"],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
for ordering, expected_output in ordering_test_cases:
|
for order_by, expected_output in order_by_test_cases:
|
||||||
with self.subTest(ordering=ordering, expected_output=expected_output):
|
with self.subTest(order_by=order_by, expected_output=expected_output):
|
||||||
values = AggregateTestModel.objects.aggregate(
|
values = AggregateTestModel.objects.aggregate(
|
||||||
arrayagg=ArrayAgg("char_field", ordering=ordering)
|
arrayagg=ArrayAgg("char_field", order_by=order_by)
|
||||||
)
|
)
|
||||||
self.assertEqual(values, {"arrayagg": expected_output})
|
self.assertEqual(values, {"arrayagg": expected_output})
|
||||||
|
|
||||||
@ -191,9 +216,9 @@ class TestGeneralAggregate(PostgreSQLTestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(values, {"arrayagg": [0, 1, 2, 0]})
|
self.assertEqual(values, {"arrayagg": [0, 1, 2, 0]})
|
||||||
|
|
||||||
def test_array_agg_integerfield_ordering(self):
|
def test_array_agg_integerfield_order_by(self):
|
||||||
values = AggregateTestModel.objects.aggregate(
|
values = AggregateTestModel.objects.aggregate(
|
||||||
arrayagg=ArrayAgg("integer_field", ordering=F("integer_field").desc())
|
arrayagg=ArrayAgg("integer_field", order_by=F("integer_field").desc())
|
||||||
)
|
)
|
||||||
self.assertEqual(values, {"arrayagg": [2, 1, 0, 0]})
|
self.assertEqual(values, {"arrayagg": [2, 1, 0, 0]})
|
||||||
|
|
||||||
@ -203,16 +228,16 @@ class TestGeneralAggregate(PostgreSQLTestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(values, {"arrayagg": [True, False, False, True]})
|
self.assertEqual(values, {"arrayagg": [True, False, False, True]})
|
||||||
|
|
||||||
def test_array_agg_booleanfield_ordering(self):
|
def test_array_agg_booleanfield_order_by(self):
|
||||||
ordering_test_cases = (
|
order_by_test_cases = (
|
||||||
(F("boolean_field").asc(), [False, False, True, True]),
|
(F("boolean_field").asc(), [False, False, True, True]),
|
||||||
(F("boolean_field").desc(), [True, True, False, False]),
|
(F("boolean_field").desc(), [True, True, False, False]),
|
||||||
(F("boolean_field"), [False, False, True, True]),
|
(F("boolean_field"), [False, False, True, True]),
|
||||||
)
|
)
|
||||||
for ordering, expected_output in ordering_test_cases:
|
for order_by, expected_output in order_by_test_cases:
|
||||||
with self.subTest(ordering=ordering, expected_output=expected_output):
|
with self.subTest(order_by=order_by, expected_output=expected_output):
|
||||||
values = AggregateTestModel.objects.aggregate(
|
values = AggregateTestModel.objects.aggregate(
|
||||||
arrayagg=ArrayAgg("boolean_field", ordering=ordering)
|
arrayagg=ArrayAgg("boolean_field", order_by=order_by)
|
||||||
)
|
)
|
||||||
self.assertEqual(values, {"arrayagg": expected_output})
|
self.assertEqual(values, {"arrayagg": expected_output})
|
||||||
|
|
||||||
@ -225,22 +250,22 @@ class TestGeneralAggregate(PostgreSQLTestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(values, {"arrayagg": ["pl", "en"]})
|
self.assertEqual(values, {"arrayagg": ["pl", "en"]})
|
||||||
|
|
||||||
def test_array_agg_jsonfield_ordering(self):
|
def test_array_agg_jsonfield_order_by(self):
|
||||||
values = AggregateTestModel.objects.aggregate(
|
values = AggregateTestModel.objects.aggregate(
|
||||||
arrayagg=ArrayAgg(
|
arrayagg=ArrayAgg(
|
||||||
KeyTransform("lang", "json_field"),
|
KeyTransform("lang", "json_field"),
|
||||||
filter=Q(json_field__lang__isnull=False),
|
filter=Q(json_field__lang__isnull=False),
|
||||||
ordering=KeyTransform("lang", "json_field"),
|
order_by=KeyTransform("lang", "json_field"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.assertEqual(values, {"arrayagg": ["en", "pl"]})
|
self.assertEqual(values, {"arrayagg": ["en", "pl"]})
|
||||||
|
|
||||||
def test_array_agg_filter_and_ordering_params(self):
|
def test_array_agg_filter_and_order_by_params(self):
|
||||||
values = AggregateTestModel.objects.aggregate(
|
values = AggregateTestModel.objects.aggregate(
|
||||||
arrayagg=ArrayAgg(
|
arrayagg=ArrayAgg(
|
||||||
"char_field",
|
"char_field",
|
||||||
filter=Q(json_field__has_key="lang"),
|
filter=Q(json_field__has_key="lang"),
|
||||||
ordering=LPad(Cast("integer_field", CharField()), 2, Value("0")),
|
order_by=LPad(Cast("integer_field", CharField()), 2, Value("0")),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.assertEqual(values, {"arrayagg": ["Foo2", "Foo4"]})
|
self.assertEqual(values, {"arrayagg": ["Foo2", "Foo4"]})
|
||||||
@ -406,8 +431,8 @@ class TestGeneralAggregate(PostgreSQLTestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(values, {"stringagg": "Text1;Text2;Text4;Text3"})
|
self.assertEqual(values, {"stringagg": "Text1;Text2;Text4;Text3"})
|
||||||
|
|
||||||
def test_string_agg_charfield_ordering(self):
|
def test_string_agg_charfield_order_by(self):
|
||||||
ordering_test_cases = (
|
order_by_test_cases = (
|
||||||
(F("char_field").desc(), "Foo4;Foo3;Foo2;Foo1"),
|
(F("char_field").desc(), "Foo4;Foo3;Foo2;Foo1"),
|
||||||
(F("char_field").asc(), "Foo1;Foo2;Foo3;Foo4"),
|
(F("char_field").asc(), "Foo1;Foo2;Foo3;Foo4"),
|
||||||
(F("char_field"), "Foo1;Foo2;Foo3;Foo4"),
|
(F("char_field"), "Foo1;Foo2;Foo3;Foo4"),
|
||||||
@ -416,19 +441,19 @@ class TestGeneralAggregate(PostgreSQLTestCase):
|
|||||||
(Concat("char_field", Value("@")), "Foo1;Foo2;Foo3;Foo4"),
|
(Concat("char_field", Value("@")), "Foo1;Foo2;Foo3;Foo4"),
|
||||||
(Concat("char_field", Value("@")).desc(), "Foo4;Foo3;Foo2;Foo1"),
|
(Concat("char_field", Value("@")).desc(), "Foo4;Foo3;Foo2;Foo1"),
|
||||||
)
|
)
|
||||||
for ordering, expected_output in ordering_test_cases:
|
for order_by, expected_output in order_by_test_cases:
|
||||||
with self.subTest(ordering=ordering, expected_output=expected_output):
|
with self.subTest(order_by=order_by, expected_output=expected_output):
|
||||||
values = AggregateTestModel.objects.aggregate(
|
values = AggregateTestModel.objects.aggregate(
|
||||||
stringagg=StringAgg("char_field", delimiter=";", ordering=ordering)
|
stringagg=StringAgg("char_field", delimiter=";", order_by=order_by)
|
||||||
)
|
)
|
||||||
self.assertEqual(values, {"stringagg": expected_output})
|
self.assertEqual(values, {"stringagg": expected_output})
|
||||||
|
|
||||||
def test_string_agg_jsonfield_ordering(self):
|
def test_string_agg_jsonfield_order_by(self):
|
||||||
values = AggregateTestModel.objects.aggregate(
|
values = AggregateTestModel.objects.aggregate(
|
||||||
stringagg=StringAgg(
|
stringagg=StringAgg(
|
||||||
KeyTextTransform("lang", "json_field"),
|
KeyTextTransform("lang", "json_field"),
|
||||||
delimiter=";",
|
delimiter=";",
|
||||||
ordering=KeyTextTransform("lang", "json_field"),
|
order_by=KeyTextTransform("lang", "json_field"),
|
||||||
output_field=CharField(),
|
output_field=CharField(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -446,7 +471,7 @@ class TestGeneralAggregate(PostgreSQLTestCase):
|
|||||||
|
|
||||||
def test_orderable_agg_alternative_fields(self):
|
def test_orderable_agg_alternative_fields(self):
|
||||||
values = AggregateTestModel.objects.aggregate(
|
values = AggregateTestModel.objects.aggregate(
|
||||||
arrayagg=ArrayAgg("integer_field", ordering=F("char_field").asc())
|
arrayagg=ArrayAgg("integer_field", order_by=F("char_field").asc())
|
||||||
)
|
)
|
||||||
self.assertEqual(values, {"arrayagg": [0, 1, 0, 2]})
|
self.assertEqual(values, {"arrayagg": [0, 1, 0, 2]})
|
||||||
|
|
||||||
@ -454,8 +479,8 @@ class TestGeneralAggregate(PostgreSQLTestCase):
|
|||||||
values = AggregateTestModel.objects.aggregate(jsonbagg=JSONBAgg("char_field"))
|
values = AggregateTestModel.objects.aggregate(jsonbagg=JSONBAgg("char_field"))
|
||||||
self.assertEqual(values, {"jsonbagg": ["Foo1", "Foo2", "Foo4", "Foo3"]})
|
self.assertEqual(values, {"jsonbagg": ["Foo1", "Foo2", "Foo4", "Foo3"]})
|
||||||
|
|
||||||
def test_jsonb_agg_charfield_ordering(self):
|
def test_jsonb_agg_charfield_order_by(self):
|
||||||
ordering_test_cases = (
|
order_by_test_cases = (
|
||||||
(F("char_field").desc(), ["Foo4", "Foo3", "Foo2", "Foo1"]),
|
(F("char_field").desc(), ["Foo4", "Foo3", "Foo2", "Foo1"]),
|
||||||
(F("char_field").asc(), ["Foo1", "Foo2", "Foo3", "Foo4"]),
|
(F("char_field").asc(), ["Foo1", "Foo2", "Foo3", "Foo4"]),
|
||||||
(F("char_field"), ["Foo1", "Foo2", "Foo3", "Foo4"]),
|
(F("char_field"), ["Foo1", "Foo2", "Foo3", "Foo4"]),
|
||||||
@ -464,38 +489,38 @@ class TestGeneralAggregate(PostgreSQLTestCase):
|
|||||||
(Concat("char_field", Value("@")), ["Foo1", "Foo2", "Foo3", "Foo4"]),
|
(Concat("char_field", Value("@")), ["Foo1", "Foo2", "Foo3", "Foo4"]),
|
||||||
(Concat("char_field", Value("@")).desc(), ["Foo4", "Foo3", "Foo2", "Foo1"]),
|
(Concat("char_field", Value("@")).desc(), ["Foo4", "Foo3", "Foo2", "Foo1"]),
|
||||||
)
|
)
|
||||||
for ordering, expected_output in ordering_test_cases:
|
for order_by, expected_output in order_by_test_cases:
|
||||||
with self.subTest(ordering=ordering, expected_output=expected_output):
|
with self.subTest(order_by=order_by, expected_output=expected_output):
|
||||||
values = AggregateTestModel.objects.aggregate(
|
values = AggregateTestModel.objects.aggregate(
|
||||||
jsonbagg=JSONBAgg("char_field", ordering=ordering),
|
jsonbagg=JSONBAgg("char_field", order_by=order_by),
|
||||||
)
|
)
|
||||||
self.assertEqual(values, {"jsonbagg": expected_output})
|
self.assertEqual(values, {"jsonbagg": expected_output})
|
||||||
|
|
||||||
def test_jsonb_agg_integerfield_ordering(self):
|
def test_jsonb_agg_integerfield_order_by(self):
|
||||||
values = AggregateTestModel.objects.aggregate(
|
values = AggregateTestModel.objects.aggregate(
|
||||||
jsonbagg=JSONBAgg("integer_field", ordering=F("integer_field").desc()),
|
jsonbagg=JSONBAgg("integer_field", order_by=F("integer_field").desc()),
|
||||||
)
|
)
|
||||||
self.assertEqual(values, {"jsonbagg": [2, 1, 0, 0]})
|
self.assertEqual(values, {"jsonbagg": [2, 1, 0, 0]})
|
||||||
|
|
||||||
def test_jsonb_agg_booleanfield_ordering(self):
|
def test_jsonb_agg_booleanfield_order_by(self):
|
||||||
ordering_test_cases = (
|
order_by_test_cases = (
|
||||||
(F("boolean_field").asc(), [False, False, True, True]),
|
(F("boolean_field").asc(), [False, False, True, True]),
|
||||||
(F("boolean_field").desc(), [True, True, False, False]),
|
(F("boolean_field").desc(), [True, True, False, False]),
|
||||||
(F("boolean_field"), [False, False, True, True]),
|
(F("boolean_field"), [False, False, True, True]),
|
||||||
)
|
)
|
||||||
for ordering, expected_output in ordering_test_cases:
|
for order_by, expected_output in order_by_test_cases:
|
||||||
with self.subTest(ordering=ordering, expected_output=expected_output):
|
with self.subTest(order_by=order_by, expected_output=expected_output):
|
||||||
values = AggregateTestModel.objects.aggregate(
|
values = AggregateTestModel.objects.aggregate(
|
||||||
jsonbagg=JSONBAgg("boolean_field", ordering=ordering),
|
jsonbagg=JSONBAgg("boolean_field", order_by=order_by),
|
||||||
)
|
)
|
||||||
self.assertEqual(values, {"jsonbagg": expected_output})
|
self.assertEqual(values, {"jsonbagg": expected_output})
|
||||||
|
|
||||||
def test_jsonb_agg_jsonfield_ordering(self):
|
def test_jsonb_agg_jsonfield_order_by(self):
|
||||||
values = AggregateTestModel.objects.aggregate(
|
values = AggregateTestModel.objects.aggregate(
|
||||||
jsonbagg=JSONBAgg(
|
jsonbagg=JSONBAgg(
|
||||||
KeyTransform("lang", "json_field"),
|
KeyTransform("lang", "json_field"),
|
||||||
filter=Q(json_field__lang__isnull=False),
|
filter=Q(json_field__lang__isnull=False),
|
||||||
ordering=KeyTransform("lang", "json_field"),
|
order_by=KeyTransform("lang", "json_field"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.assertEqual(values, {"jsonbagg": ["en", "pl"]})
|
self.assertEqual(values, {"jsonbagg": ["en", "pl"]})
|
||||||
@ -533,7 +558,7 @@ class TestGeneralAggregate(PostgreSQLTestCase):
|
|||||||
Room.objects.annotate(
|
Room.objects.annotate(
|
||||||
requirements=JSONBAgg(
|
requirements=JSONBAgg(
|
||||||
"hotelreservation__requirements",
|
"hotelreservation__requirements",
|
||||||
ordering="-hotelreservation__start",
|
order_by="-hotelreservation__start",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.filter(requirements__0__sea_view=True)
|
.filter(requirements__0__sea_view=True)
|
||||||
@ -552,7 +577,7 @@ class TestGeneralAggregate(PostgreSQLTestCase):
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_string_agg_array_agg_ordering_in_subquery(self):
|
def test_string_agg_array_agg_order_by_in_subquery(self):
|
||||||
stats = []
|
stats = []
|
||||||
for i, agg in enumerate(AggregateTestModel.objects.order_by("char_field")):
|
for i, agg in enumerate(AggregateTestModel.objects.order_by("char_field")):
|
||||||
stats.append(StatTestModel(related_field=agg, int1=i, int2=i + 1))
|
stats.append(StatTestModel(related_field=agg, int1=i, int2=i + 1))
|
||||||
@ -561,7 +586,7 @@ class TestGeneralAggregate(PostgreSQLTestCase):
|
|||||||
|
|
||||||
for aggregate, expected_result in (
|
for aggregate, expected_result in (
|
||||||
(
|
(
|
||||||
ArrayAgg("stattestmodel__int1", ordering="-stattestmodel__int2"),
|
ArrayAgg("stattestmodel__int1", order_by="-stattestmodel__int2"),
|
||||||
[
|
[
|
||||||
("Foo1", [0, 1]),
|
("Foo1", [0, 1]),
|
||||||
("Foo2", [1, 2]),
|
("Foo2", [1, 2]),
|
||||||
@ -573,7 +598,7 @@ class TestGeneralAggregate(PostgreSQLTestCase):
|
|||||||
StringAgg(
|
StringAgg(
|
||||||
Cast("stattestmodel__int1", CharField()),
|
Cast("stattestmodel__int1", CharField()),
|
||||||
delimiter=";",
|
delimiter=";",
|
||||||
ordering="-stattestmodel__int2",
|
order_by="-stattestmodel__int2",
|
||||||
),
|
),
|
||||||
[("Foo1", "0;1"), ("Foo2", "1;2"), ("Foo3", "2;3"), ("Foo4", "3;4")],
|
[("Foo1", "0;1"), ("Foo2", "1;2"), ("Foo3", "2;3"), ("Foo4", "3;4")],
|
||||||
),
|
),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user