Как использовать FilteredRelation с OuterRef?

Я пытаюсь использовать Django ORM для создания набора запросов и не могу найти, как использовать OuterRef в условии присоединения с FilteredRelation.

Что у меня есть в Django

Основной набор запросов

queryset = LineOutlier.objects.filter(report=self.kwargs['report_pk'], report__apn__customer__cen_id=self.kwargs['customer_cen_id']) \
                    .select_related('category__traffic') \
                    .select_related('category__frequency') \
                    .select_related('category__stability') \
                    .prefetch_related('category__traffic__labels') \
                    .prefetch_related('category__frequency__labels') \
                    .prefetch_related('category__stability__labels') \
                    .annotate(history=subquery)

Подзапрос

subquery = ArraySubquery(
            LineOutlierReport.objects
            .filter((Q(lineoutlier__imsi=OuterRef('imsi')) | Q(lineoutlier__isnull=True)) & Q(id__in=last_5_reports_ids))
             .values(json=JSONObject(
                            severity='lineoutlier__severity',
                            report_id='id',
                            report_start_date='start_date',
                            report_end_date='end_date'
                            )
                    )
        )

Запрос может быть выполнен, но сгенерированный SQL не совсем то, что я хочу :

SQL Generated

SELECT "mlformalima_lineoutlier"."id",
       "mlformalima_lineoutlier"."imsi",
       ARRAY(
        SELECT JSONB_BUILD_OBJECT('severity', V1."severity", 'report_id', V0."id", 'report_start_date', V0."start_date", 'report_end_date', V0."end_date") AS "json"
          FROM "mlformalima_lineoutlierreport" V0
          LEFT OUTER JOIN "mlformalima_lineoutlier" V1
            ON (V0."id" = V1."report_id")
         WHERE ((V1."imsi" = ("mlformalima_lineoutlier"."imsi") OR V1."id" IS NULL) AND V0."id" IN (SELECT DISTINCT ON (U0."id") U0."id" FROM "mlformalima_lineoutlierreport" U0 WHERE U0."apn_id" = 2 ORDER BY U0."id" ASC, U0."end_date" DESC LIMIT 5))
       ) AS "history",
  FROM "mlformalima_lineoutlier"

Проблема здесь в том, что условие OuterRef (V1."imsi" = ("mlformalima_lineoutlier"."imsi")) выполняется на операторе WHERE, а я хочу, чтобы оно было на операторе JOIN

Что я хочу в SQL

SELECT "mlformalima_lineoutlier"."id",
       "mlformalima_lineoutlier"."imsi",
       ARRAY(
        SELECT JSONB_BUILD_OBJECT('severity', V1."severity", 'report_id', V0."id", 'report_start_date', V0."start_date", 'report_end_date', V0."end_date") AS "json"
          FROM "mlformalima_lineoutlierreport" V0
          LEFT OUTER JOIN "mlformalima_lineoutlier" V1
            ON (V0."id" = V1."report_id" AND ((V1."id" IS NULL) OR V1."imsi" = ("mlformalima_lineoutlier"."imsi")))
         WHERE V0."id" IN (SELECT DISTINCT ON (U0."id") U0."id" FROM "mlformalima_lineoutlierreport" U0 WHERE U0."apn_id" = 2 ORDER BY U0."id" ASC, U0."end_date" DESC LIMIT 5))
       ) AS "history",
  FROM "mlformalima_lineoutlier"

Что я пробовал в Django

Я пытался использовать FilteredRelation для изменения условия JOIN, но не могу использовать его в сочетании с OuterRef

subquery = ArraySubquery(
            LineOutlierReport.objects
            .annotate(filtered_relation=FilteredRelation('lineoutlier', condition=Q(lineoutlier__imsi=OuterRef('imsi')) | Q(lineoutlier__isnull=True)))
            .filter(Q(id__in=last_5_reports_ids))
            .values(json=JSONObject(
                            severity='filtered_relation__severity',
                            report_id='id',
                            report_start_date='start_date',
                            report_end_date='end_date'
                            )
                    )
        )

Я не могу выполнить этот запрос из-за следующей ошибки

ValueError: This queryset contains a reference to an outer query and may only be used in a subquery.

Как я могу изменить свой запрос, чтобы он работал?

Это похоже на эту ошибку Django. В качестве обходного пути вы можете аннотировать другой столбец и сослаться на него в FilteredRelation, например, так :

subquery = ArraySubquery(
        LineOutlierReport.objects
        .annotate(
            outer_imsi=OuterRef('imsi'),
            filtered_relation=FilteredRelation('lineoutlier', condition=Q(lineoutlier__imsi=F('outer_imsi')) | Q(lineoutlier__isnull=True)))
        .filter(Q(id__in=last_5_reports_ids))
        .values(json=JSONObject(
                        severity='filtered_relation__severity',
                        report_id='id',
                        report_start_date='start_date',
                        report_end_date='end_date'
                        )
                )
    )

Таким образом, вы избежите обработки OuterRef внутри FilteredRelation.

Вернуться на верх