Using StringAgg with the distinct=True
argument works under normal circumstances, e.g.:
entities = entities.annotate(roles=StringAgg(
"credits__role__name",
delimiter=", ",
distinct=True,
ordering="credits__role__name"
))
But when used with a conditional expression
, it throws the exception django.db.utils.ProgrammingError: in an aggregate with DISTINCT, ORDER BY expressions must appear in argument list
, e.g.:
releases = releases.annotate(credits_primary=StringAgg(
Case(When(credits__type="primary", then="credits__entity__name")),
delimiter=", ",
distinct=True,
ordering="credits__entity__name"
))
Why is this, and is there a way to make the second example work?
Edit: here is the full code sample that I'm using currently and works. What I want to do is eliminate duplicate entity
s from the aggregated list string.
if request.GET.get("order[0][name]"):
order = request.GET["order[0][name]"]
if order == "credits_primary":
releases = releases.annotate(credits_primary=StringAgg(
Case(When(credits__type="primary", then="credits__entity__name")),
delimiter=", ",
ordering="credits__entity__name"
))
elif order == "credits_secondary":
releases = releases.annotate(credits_secondary=StringAgg(
Case(When(credits__type="secondary", then="credits__entity__name")),
delimiter=", ",
ordering="credits__entity__name"
))
else:
order = "title"
# Order releases
if request.GET.get("order[0][dir]") == "desc":
releases = releases.order_by(F(order).desc(nulls_last=True), "title")
else:
releases = releases.order_by(F(order).asc(nulls_last=True), "title")
Note that a release
may not have any primary or secondary credits, and one entity
may have more than one primary or secondary credit for the same release
(with different role
s).
Try the following query, it should give the expected results:
from django.db import models
from django.contrib.postgres.aggregates import StringAgg
releases.annotate(
credits_primary=StringAgg(
'credits__entity__name',
# or `models.Q(credits__type='secondary')` in elif condition
filter=models.Q(credits__type='primary'),
delimiter=', ',
distinct=True,
ordering='credits__entity__name',
),
)
You should use filter
inside StringAgg
, I didn't find such an example in the documentation for StringAgg
, but you can see it here by looking at the signature. On this page you can see the use of filter
in the Count
aggregation function.
...
from django.db.models import Q
above_5 = Count("book", filter=Q(book__rating__gt=5))
...