Usar StringAgg com o distinct=True
argumento funciona em circunstâncias normais, por exemplo:
entities = entities.annotate(roles=StringAgg(
"credits__role__name",
delimiter=", ",
distinct=True,
ordering="credits__role__name"
))
Mas quando usado com uma condicional expression
, ele lança a exceção django.db.utils.ProgrammingError: in an aggregate with DISTINCT, ORDER BY expressions must appear in argument list
, por exemplo:
releases = releases.annotate(credits_primary=StringAgg(
Case(When(credits__type="primary", then="credits__entity__name")),
delimiter=", ",
distinct=True,
ordering="credits__entity__name"
))
Por que isso acontece e há uma maneira de fazer o segundo exemplo funcionar?
Editar: aqui está o exemplo de código completo que estou usando atualmente e funciona. O que eu quero fazer é eliminar entity
s duplicados da string da lista agregada.
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")
Observe que um aluno release
não pode ter créditos primários ou secundários, e entity
pode ter mais de um crédito primário ou secundário para o mesmo aluno release
(com role
créditos diferentes).
Tente a seguinte consulta, ela deve dar os resultados esperados:
Você deve usar
filter
insideStringAgg
, não encontrei tal exemplo na documentação paraStringAgg
, mas você pode vê-lo aqui olhando a assinatura. Nesta página você pode ver o uso defilter
naCount
função de agregação.Edição: veja esta resposta para uma solução mais elegante e legível.
Por razões que francamente não entendo, ele parece classificar por
credits__entity__name
mesmo quandoordering="credits__entity__name"
é omitido. Talvez por padrão ele ordene alfabeticamente, ou pelo campo cujo valor é passado no conditionalexpression
. A documentação do Django não especifica o comportamento padrão. (note que aCredit
ordenação padrão do modelo não é por entidade)Em outras palavras, isso produz o resultado desejado:
Espero que alguém com um conhecimento mais profundo de Django e/ou PostgreSQL possa produzir uma resposta que explique por que isso acontece.
O erro ocorre porque o PostgreSQL exige que
ORDER BY
as colunas apareçam emDISTINCT
.garanta que tanto a
ORDER BY
expressão quanto oDISTINCT
argumento estejam alinhados. Você pode usarFilter
para pré-filtrar antes da agregação, assim:Alternativa: Use
Filter
Em vez deCase
Se suaCase
expressão estiver agindo como umfilter
, outra abordagem é usar Filter diretamente na agregação:Diga-me se ajudou.