postgresql Django ORM:基于另一个左连接的左连接条件

m528fe3b  于 2023-03-12  发布在  PostgreSQL
关注(0)|答案(1)|浏览(122)

我使用Django 4.0和PostgreSQL(13.7)作为后端(如果解决方案需要的话,可以升级)
我有两个模型:CatAttributeAttribute包含描述Cat示例的通用键值对。
Cat表格:
| PK参数|姓名|
| - ------|- ------|
| 1个|万达|
| 第二章|爱莎|
| 三个|塔拉|
Attribute表格:
| PK参数|目录号|键|价值|
| - ------|- ------|- ------|- ------|
| 1个|1个|大小|十个|
| 第二章|1个|年龄|五个|
| 三个|第二章|大小|七|
| 四个|第二章|智力|七十五|
| 五个|第二章|儿童|三个|
| 六个|三个|智力|六十|
| 七|三个|年龄|九|
我希望根据所有示例的其他属性的条件选择不同的属性值,例如:
如果Catage大于42大于children,则获取Catsize-或者如果不存在匹配,则如果intelligence大于50,则获取age
这里的示例包含随机属性和数字,没有任何意义,但在我的应用程序中,条件可能过于复杂,包括几个递归AND、OR、EXISTS和NOT条件。
我的疑问是:

SELECT
  DISTINCT(cat.id),
  cat.name,
  COALESCE(
    result1.key,
    result2.key
  ) as result_key
  COALESCE(
    result1.value,
    result2.value
  ) as result_value
FROM cat
-- first condition
LEFT OUTER JOIN attribute cond1 ON (
   cat.id = cond1.cat_id AND (
    cond1.key = 'age' AND cond1.value > 4 OR cond1.key = 'children' and cond1.value > 2
  )
-- second condition
LEFT OUTER JOIN attribute cond2 ON (
   cat.id = cond2.cat_id AND (
    cond2.key = 'intelligence' AND cond2.value > 50
  )
)
-- choose the one or other attribute depending on the first two conditions
LEFT OUTER JOIN attribute result1 ON (
  cat.id = result1.cat_id AND (cond1.cat_id IS NOT NULL) AND result1.key = 'size'
)
LEFT OUTER JOIN attribute result2 ON (
  cat.id = result2.cat_id AND (cond2.cat_id IS NOT NULL) AND result2.key = 'intelligence'
)

结果应为:
| PK参数|姓名|结果键|结果值|
| - ------|- ------|- ------|- ------|
| 1个|万达|大小|十个|
| 第二章|爱莎|大小|七|
| 三个|塔拉|年龄|九|
使用Django的ORM,我尝试了以下方法:

Cat.objects.annotate(
    cond1=FilteredRelation(
        "attribute",
        condition=(
            Q(attribute__key="age", attribute__value__gt=4)
            | Q(attribute__key="children", attribute__value__gt=2)
        ),
    ),
    cond2=FilteredRelation(
        "attribute",
        condition=(Q(attribute__key="intelligence", attribute__value__gt=50)),
    ),
    result1=FilteredRelation(
        "attribute",
        condition=(Q(cond1__cat_id__isnull=False, attribute__key="size")),
    ),
    result2=FilteredRelation(
        "attribute",
        condition=(Q(cond2__cat_id__isnull=False, attribute__key="age")),
    ),
    result_key=Coalesce(F("result1__key"), F("result2__key")),
    result_value=Coalesce(F("result1__value"), F("result2__value")),
).distinct("pk")

它将失败,并显示以下错误:

ValueError: FilteredRelation's condition doesn't support
relations outside the 'attribute' (got 
'cond1__cat_id__isnull').

是否有可能构造这个或任何其他查询来获得预期的结果,或者这是Django的硬限制,只有使用原始查询才是解决这个问题的最后手段?

8yoxcaq7

8yoxcaq71#

我已经结束了在SQL中使用CASE/WHEN语句,因为看起来Django不支持我的问题中描述的用例(如错误消息所述)。
生成的ORM查询将为:

Cat.objects.annotate(
    cond1=FilteredRelation(
        "attribute",
        condition=(
            Q(attribute__key="age", attribute__value__gt=4)
            | Q(attribute__key="children", attribute__value__gt=2)
        ),
    ),
    cond2=FilteredRelation(
        "attribute",
        condition=(Q(attribute__key="intelligence", attribute__value__gt=50)),
    ),
    result1=FilteredRelation(
        "attribute",
        condition=(Q(attribute__key="size")),
    ),
    result2=FilteredRelation(
        "attribute",
        condition=(Q(attribute__key="age")),
    ),
    result=Case(
        When(cond1__cat_id__isnull=False, then=F("result1__value"))
        When(cond2__cat_id__isnull=False, then=F("result2__value"))
    ),
).distinct("pk")

相关问题