我使用Django 4.0和PostgreSQL(13.7)作为后端(如果解决方案需要的话,可以升级)
我有两个模型:Cat
和Attribute
。Attribute
包含描述Cat
示例的通用键值对。Cat
表格:
| PK参数|姓名|
| - ------|- ------|
| 1个|万达|
| 第二章|爱莎|
| 三个|塔拉|Attribute
表格:
| PK参数|目录号|键|价值|
| - ------|- ------|- ------|- ------|
| 1个|1个|大小|十个|
| 第二章|1个|年龄|五个|
| 三个|第二章|大小|七|
| 四个|第二章|智力|七十五|
| 五个|第二章|儿童|三个|
| 六个|三个|智力|六十|
| 七|三个|年龄|九|
我希望根据所有示例的其他属性的条件选择不同的属性值,例如:
如果Cat
的age
大于4
或2
大于children
,则获取Cat
的size
-或者如果不存在匹配,则如果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的硬限制,只有使用原始查询才是解决这个问题的最后手段?
1条答案
按热度按时间8yoxcaq71#
我已经结束了在SQL中使用CASE/WHEN语句,因为看起来Django不支持我的问题中描述的用例(如错误消息所述)。
生成的ORM查询将为: