我试图将Django ORM用于SQL中需要JOIN
的任务。一世
已经有一个解决方法,可以通过多个查询完成相同的任务
和一些非DB处理,但我对运行时复杂性不满意。
首先,我想简要介绍一下我的相关部分 模型。在那之后,我将用英语,SQL和(低效)Django ORM解释任务。
在我的CMS模型中,帖子是多语言的:对于每个帖子和每种语言,可以有一个帖子内容的实例。此外,在编辑帖子时,我不是UPDATE
,而是INSERT
新版本。
因此,PostContent
在post
,language
和version
上是唯一的。这是班级:
class PostContent(models.Model):
""" contains all versions of a post, in all languages. """
language = models.ForeignKey(Language)
post = models.ForeignKey(Post) # the Post object itself only
version = models.IntegerField(default=0) # contains slug and id.
# further metadata and content left out
class Meta:
unique_together = (("resource", "language", "version"),)
这就是任务:我希望使用ORM获取每种语言中所有帖子的最新版本列表。在SQL中,这会转换为{在JOIN
和GROUP BY
的子查询上{1}},以获得MAX
和version
的每对唯一对的resource
的最大值。这个问题的完美答案是许多ORM调用产生以下SQL语句:
language
我目前使用Django ORM的解决方案不会产生这样的JOIN,而是两个单独的SQL
查询,其中一个查询可能变得非常大。我首先执行子查询(上面的内部SELECT
id,
post_id,
version,
v
FROM
cms_postcontent,
(SELECT
post_id as p,
max(version) as v,
language_id as l
FROM
cms_postcontent
GROUP BY
post_id,
language_id
) as maxv
WHERE
post_id=p
AND version=v
AND language_id=l;
):
SELECT
现在,我没有加入maxv = PostContent.objects.values('post','language').annotate(
max_version=Max('version'))
,而是明确要求maxv
中的每一个帖子
为maxv
的每个元组筛选PostContent.objects.all()
。生成的SQL看起来像
post, language, max_version
在Django中:
SELECT * FROM PostContent WHERE
post=P1 and language=L1 and version=V1
OR post=P2 and language=L2 and version=V2
OR ...;
如果from django.db.models import Q
conjunc = map(lambda pc: Q(version=pc['max_version']).__and__(
Q(post=pc['post']).__and__(
Q(language=pc['language']))), maxv)
result = PostContent.objects.filter(
reduce(lambda disjunc, x: disjunc.__or__(x), conjunc[1:], conjunc[0]))
足够小,例如在检索单个帖子时,这可能是
一个很好的解决方案,但查询的大小和创建它的时间线性增长
帖子数量。解析查询的复杂性至少也是线性的。
除了使用原始SQL之外,还有更好的方法吗?
答案 0 :(得分:0)
只要查询集查询相同的模型,您就可以使用|
运算符加入(在联合意义上)查询集。
然而,听起来你想要像PostContent.objects.order_by('version').distinct('language')
这样的东西;由于您在1.3.1中无法做到这一点,因此请考虑将values
与distinct()
结合使用以获得所需的效果。