Django When(...) object SQL отсутствует при использовании с __in lookup с пустым списком в качестве значения, что приводит к ProgrammingError
используя Django 3.0 (будет обновлен), я получаю некорректный SQL, который приводит к django.db.utils.ProgrammingError
, и мне интересно, может ли кто-нибудь помочь мне понять почему.
Интересно, возникает ли эта проблема и в более новых версиях Django? Но я надеюсь, что смогу заставить его работать без необходимости сразу же обновлять Django.
Запрос модели Я аннотирую поле к каждому экземпляру и использую Case
и When
с этим, и кажется, что ошибка происходит при использовании отрицаемого объекта Q
с поиском __in
, который получает пустой список в качестве значения.
Я покажу три примера, которые очень похожи, но приводят к разным SQL. Только первый пример приводит к ошибочному SQL.
Пример запроса, который приводит к некорректному SQL:
Activity.objects.all()
.annotate(
employee_authorized=Case(
When(~Q(case__assigned_unit__in=[]), then=Value(False)),
default=Value(True),
output_field=BooleanField(),
)
)
Это приводит к следующему SQL, где нет ничего между WHEN
и THEN
. В результате получается django.db.utils.ProgrammingError
.
SELECT "activity_activity"."id",
...
CASE WHEN THEN False ELSE True END AS "employee_authorized"
FROM "activity_activity"
LEFT OUTER JOIN "case_case" ON ("activity_activity"."case_id" = "case_case"."id")
Приведенный ниже пример, похоже, приводит к логическому SQL. Единственное отличие заключается в том, что объект Q
не отрицается.
Activity.objects.all()
.annotate(
employee_authorized=Case(
When(Q(case__assigned_unit__in=[]), then=Value(False)),
default=Value(True),
output_field=BooleanField(),
)
)
SQL:
SELECT "activity_activity"."id",
...
True AS "employee_authorized"
FROM "activity_activity"
LEFT OUTER JOIN "case_case" ON ("activity_activity"."case_id" = "case_case"."id")
Еще один пример, который действительно приводит к логическому SQL, при этом отрицая объект Q
, но используя список, который не является пустым.
Activity.objects.all()
.annotate(
employee_authorized=Case(
When(~Q(case__assigned_unit__in=[OrgUnit.objects.first()]), then=Value(False)),then=Value(False)),
default=Value(True),
output_field=BooleanField(),
)
)
Что приводит к следующему SQL:
SELECT "activity_activity"."id",
...
CASE
WHEN NOT ("case_case"."assigned_unit_id" IN (251) AND "case_case"."assigned_unit_id" IS NOT NULL) THEN False
ELSE True END AS "employee_authorized"
FROM "activity_activity"
LEFT OUTER JOIN "case_case" ON ("activity_activity"."case_id" = "case_case"."id")
<
Одна из идей, как избежать этого, - "переключить" условие по умолчанию и использовать Q() вместо ~Q()
.annotate(
employee_authorized=Case(
When(Q(case__assigned_unit__in=[]), then=Value(True)),
default=Value(False),
output_field=BooleanField(),
)
)