首页 > 解决方案 > Django When(...) object SQL 与 __in 一起使用时缺少 SQL,空列表作为值,导致 ProgrammingError

问题描述

使用 Django 3.0(将升级)我得到一些导致 a 的无效 SQL,django.db.utils.ProgrammingError我想知道是否有人可以帮助我了解原因。

我想知道这个问题是否也出现在较新版本的 Django 中。但我希望在不必立即升级 Django 的情况下让它工作。

查询模型我正在为每个实例注释一个字段,并使用CaseWhen,并且似乎在使用带有查找空列表作为值的否定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")

所以。我看不出我在这里做错了什么或是否做错了什么。希望有人可以帮助我完成这项工作。

标签: sqldjangopostgresqldjango-orm

解决方案


One idea to avoid this happening is to "switch" the default condition and use Q() instead of ~Q()

.annotate(
    employee_authorized=Case(
        When(Q(case__assigned_unit__in=[]), then=Value(True)),
        default=Value(False),
        output_field=BooleanField(),
    )
)

推荐阅读