I'm trying to implement a subquery with Django ORM, but I can't find a working solution. The SQL query that I would need to reverse-engineer is:
select t1.location, sum(t1.value_relative::numeric) as total
from (
select
administrative_division_id as "location",
jsonb_array_elements("values"->'loss'->'values')->>'dim_value' as dim_value,
jsonb_array_elements("values"->'loss'->'values')->>'value_relative' as value_relative
from assessments_entrybaserisk
where "values"->'scenario'->>'value' = 'Total'
) t1
where t1.dim_value::numeric = 2
group by t1.location
I tried to use Django Subquery like below:
subq = EntryBaseRisk.objects \
.filter(id=OuterRef('pk'), values__scenario__value='Total') \
.annotate(
_dim_value=Cast(
KeyTextTransform('dim_value',
JsonbArrayElements(KeyTransform('values', KeyTransform('loss', 'values')))),
IntegerField()),
_value_relative=Cast(
KeyTextTransform('value_relative',
JsonbArrayElements(KeyTransform('values', KeyTransform('loss', 'values')))),
FloatField())) \
.values('assessment', 'administrative_division', '_dim_value', '_value_relative')
data = EntryBaseRisk.objects \
.annotate(
dim_value=Subquery(subq.values('_dim_value'), output_field=IntegerField()),
total_relative_value=Sum(Subquery(subq.values('_value_relative'),
output_field=FloatField()))) \
.filter(**filters) \
.values('administrative_division', 'total_relative_value') \
.order_by('administrative_division')
but I get an error ProgrammingError: more than one row returned by a subquery used as an expression
.
The problem is that SQL generated by Django is different from my SQL above, because it doesn't select from a select and it works only if the subquery is returning only one row.
I can't perform the Sum aggregation in the subquery, because aggregate function calls cannot contain set-returning function calls.
Is there another way to make Django generate an SQL as per my needs?