Django合并表列的最大值
我试图通过合并两个独立表的结果来获得最大值。我正在尝试获取一篇帖子的最新评论。我有一个模型注释,可以通过Post.comments访问。我还有一个独立于Post的body,Post.body可以访问它。当注释和正文都是DateTimeField时,它们都有一个字段。我想返回一个按最新活动排序的查询集,这样,带有最新评论或正文的帖子将首先显示 假设模型如下所示:Django合并表列的最大值,django,join,merge,union,annotate,Django,Join,Merge,Union,Annotate,我试图通过合并两个独立表的结果来获得最大值。我正在尝试获取一篇帖子的最新评论。我有一个模型注释,可以通过Post.comments访问。我还有一个独立于Post的body,Post.body可以访问它。当注释和正文都是DateTimeField时,它们都有一个字段。我想返回一个按最新活动排序的查询集,这样,带有最新评论或正文的帖子将首先显示 假设模型如下所示: class Body(models.Model): when = models.DateTimeField(default=da
class Body(models.Model):
when = models.DateTimeField(default=datetime.datetime.now)
class Post(models.Model):
body = models.ForeignKey(Body)
class Comment(models.Model):
post = models.ForeignKey(Post, related_name='comments')
when = models.DateTimeField(default=datetime.datetime.now)
如果可能的话,我希望结果仍然是查询集,因为我会继续处理它,并进一步将其传递到分页器以限制结果
基本上,我希望能够打电话:
q = Post.annotate(
activity=Max(
Union('comments__when', 'body__when')
)
)
q = q.order_by('-activity')
但我不知道如何实现这种结合
我相信实现我所寻找的目标的SQL可以与:
SELECT
...
IF(MAX(c.`when`), MAX(c.`when`), b.`when`) AS `activity`
...
FROM `post` p
...
LEFT OUTER JOIN `comment` AS c
ON c.`post_id`=p.`id`
LEFT OUTER JOIN `body` AS b
ON p.`body_id`=b.`id`
...
这样一个定制的注释和连接可以实现吗?我花了很长时间才弄明白。一个大问题是Django不支持JOIN语句ON子句的多个条件。由于单个SELECT语句依赖于两个单独的联接,因此我们无法准确跟踪表名。当你要求django注释一个表时,比如用Max,你最终得到的条件是MaxT7.When。。。将联接表省略为T7,其中T7不一致。因此,当给定Django自动生成的联接时,我需要一种累积生成表达式MaxT7.0的方法。许多在线帖子都告诉您在QuerySet上使用.raw支持,但是在这种情况下,使用ORM会失去很多好处 我提出的解决方案是生成一个自定义聚合函数。我调用了这个函数CustomMax: 该功能的用途是:
q = Post.annotate(
activity=CustomMax(
'comments__when',
alt_table="app_post",
alt_field="when",
)
)
q.query.join((
'app_post',
'app_comment',
'id',
'post_id',
))
q = q.order_by('-activity')
我包括.join以允许alt_表作为一个join存在,Django将自动处理Max部分的SELECT和join语句的命名。此用法生成的SQL类似于:
SELECT
...
GREATEST(app_post.when, IFNULL(MAX(T7.`when`), app_post.when)) AS `activity`,
...
`app_post`.`id`
...
INNER JOIN `app_post` ON ...
...
LEFT OUTER JOIN `app_comment` T7 ON (`app_post`.`id` = T7.`post_id`)
...
注意:这仅适用于上面的帖子和评论模型。我的实际实现有点复杂,需要这个解决方案
如果Django团队开发了这个建议补丁,将join语句包含在其中,那么这也会容易得多。额外:
SELECT
...
GREATEST(app_post.when, IFNULL(MAX(T7.`when`), app_post.when)) AS `activity`,
...
`app_post`.`id`
...
INNER JOIN `app_post` ON ...
...
LEFT OUTER JOIN `app_comment` T7 ON (`app_post`.`id` = T7.`post_id`)
...