使用 Django 的 ORM Extra() 包含重复表

Posted

技术标签:

【中文标题】使用 Django 的 ORM Extra() 包含重复表【英文标题】:Including Duplicate Tables using Django's ORM Extra() 【发布时间】:2011-10-15 11:22:03 【问题描述】:

我正在尝试使用 Django 的 ORM 实现一个简单的三元组。我希望能够搜索任意复杂的三元组模式(例如,就像使用 SparQL 一样)。

为此,我尝试使用.extra() 方法。然而,即使文档提到它理论上可以通过自动为重复的表引用创建别名来处理对同一个表的重复引用,但我发现它在实践中并没有这样做。

例如,假设我的“三重”应用中有以下模型:

class Triple(models.Model):
    subject = models.CharField(max_length=100)
    predicate = models.CharField(max_length=100)
    object = models.CharField(max_length=100)

我的数据库中存储了以下三元组:

subject predicate object
bob has-a hat .
bob knows sue .
sue has-a house .
bob knows tom .

现在,假设我想查询 bob 知道谁有房子的每个人的名字。在 SQL 中,我会这样做:

SELECT t2.subject AS name
FROM triple_triple t1
INNER JOIN triple_triple t2 ON
    t1.subject = 'bob'
AND t1.predicate = 'knows'
AND t1.object = t2.subject
AND t2.predicate = 'has-a'
AND t2.object = 'house'

我不完全确定 Django 的 ORM 会是什么样子,尽管我认为它会是这样的:

q = Triple.objects.filter(subject='bob', predicate='knows')
q = q.extra(tables=['triple_triple'], where=["triple_triple.object=t1.subject AND t1.predicate = 'has-a' AND t1.object = 'house'"])
q.values('t1.subject')

很遗憾,此操作失败并显示错误“DatabaseError: no such column: t1.subject”

运行 print q.query 显示:

SELECT "triple_triple"."subject" FROM "triple_triple" WHERE ("triple_triple"."subject" = 'bob' AND "triple_triple"."predicate" = 'knows'
AND triple_triple.object = t1.subject AND t1.predicate = 'has-a' AND t1.object = 'house')

这似乎表明我对 .extra() 的调用中的表参数被忽略了,因为没有在任何地方插入对 triple_triple 的第二次引用。

为什么会这样?使用 Django 的 ORM 引用同一表中记录之间的复杂关系的适当方法是什么?

编辑:我发现这个 useful snippet 通过 .extra() 包含自定义 SQL,以便在模型管理器中使用它。

【问题讨论】:

【参考方案1】:

我认为您缺少的是 select 参数(用于额外方法)

这似乎有效:

qs = Triple.objects.filter(subject="bob", predicate="knows").extra(
          select='known': "t1.subject",
          tables=['"triple_triple" AS "t1"'],
          where=['''triple_triple.object=t1.subject 
                    AND t1.predicate="has-a" AND t1.object="'''])

qs.values("known")

【讨论】:

谢谢。奇怪的是,它会仅仅因为它没有在 SELECT 中引用而删除附加表,因为当第二个表用于在 WHERE 子句中进行过滤时,这会中断查询。可能的错误? @Cerin:我不认为这是一个错误,我对此的肮脏解释是您使用 where 参数来获得更多条件,并使用 select 向返回的查询集添加更多列。 @Tommaso:我的意思是我似乎不能使用多个表,除非我从每个表中选择至少一列。这没有任何意义,因为我不会总是想要或需要从我想要加入的每个表中选择列。这就是为什么我认为这要么是一个错误,要么是一个设计不佳的功能。 上面的代码真的适合你吗?我的 Django 转义了给 extra() 的表,所以 tables=['"triple_triple" AS "t1"'] 首先不起作用。【参考方案2】:

我遇到了同样的问题,Django 转义(添加反引号)到我的表名,这意味着我无法手动添加别名;生成的 FROM 子句如下所示:

"mytable" AS T100

但同时,如果表已经被提及,Django 不会自动为你创建别名;相反,它忽略了表,只是添加了 WHERE 子句,就好像它们引用了原始表一样。

Django 1.8 的文档表明 .extra() 将为您创建别名:

https://docs.djangoproject.com/en/1.8/ref/models/querysets/#django.db.models.query.QuerySet.extra

但我的查询似乎不是这种情况,可能是因为原始表是 LEFT OUTER JOIN 的一部分,而不是简单的 FROM x,y,z 子句。

【讨论】:

以上是关于使用 Django 的 ORM Extra() 包含重复表的主要内容,如果未能解决你的问题,请参考以下文章

Django 补充ORM的extra过滤

Django ORM的F Q和extra操作

使用 Extra 和 Filter 的 Django OR 查询

使用 Django ORM 的产品索引

Django ORM 替换额外的 where

内连接子查询 Django ORM 等效