【问题标题】:How to annotate Django QuerySet with other object using Subquery如何使用子查询用其他对象注释 Django QuerySet
【发布时间】:2017-11-30 16:51:42
【问题描述】:

在 Django 1.11 版中,添加了Subquery expressions。我希望使用此功能根据一些过滤器选择相关的模型对象。

这是来自documentation的示例:

from django.db.models import OuterRef, Subquery
newest = Comment.objects.filter(post=OuterRef('pk')).order_by('-created_at')   
Post.objects.annotate(newest_commenter_email=Subquery(newest.values('email')[:1]))

我也想做同样的事情,但在这种情况下,我希望将整个 Comment 对象保存到 newest_comment 注释中,而不是仅使用“newest_commenter_email”进行注释,如下所示:

from django.db.models import OuterRef, Subquery
newest = Comment.objects.filter(post=OuterRef('pk')).order_by('-created_at')   
Post.objects.annotate(newest_comment=Subquery(newest[:1]))

但是,这会导致以下错误:

FieldError: Expression contains mixed types. You must set output_field

有没有办法解决这个问题?

【问题讨论】:

  • 也许你可以从评论模型开始? Comment.objects.filter(post=pk).order_by('-created_at').select_related('post')[:1]。然后,您使用最新评论预加载了 Post 对象。这样你就可以得到整个 Post and Comment 模型。

标签: python django


【解决方案1】:

你在子查询中省略了values,试试这个:

Post.objects.annotate(newest_commenter=Subquery(newest.values('author')[:1]))

其中“作者”是评论模型上的“评论者”字段。

【讨论】:

  • 我认为这是有意的。他想要注释整个对象,而不仅仅是单个“作者”字段。
【解决方案2】:

你做不到。您只能使用基本类型值进行注释,而不能使用对象。其基本原因是SQL查询的结果是一个表,其所有单元格都是基本值,而不是嵌套表。 为了做你想做的事情,我们需要 SQL 来支持嵌套表和 Django 能够将嵌套表转换为嵌套对象。我认为这永远不会发生。 因此,为了获得相关对象的多个字段,您应该为每个字段重复注释:

Post.objects.annotate(
    newest_commenter_email=Subquery(newest.values('email')[:1]),
    newest_commenter_username=Subquery(newest.values('username')[:1]),
)

不要害怕由于对每个字段执行相同的查询而导致的性能问题。 SQL 引擎足够聪明,可以看到查询是相同的并缓存它的值

【讨论】:

    【解决方案3】:

    按照 Brobin 的建议 -

    Comment.objects.filter(post=pk).order_by('created_at').select_related('post')[:1]
    

    如果你真的想摆脱 Post 模型,尽管这样做 -

    
    q = (
      Comment
      .objects
      .filter(
        pk=Subquery(
          Comment
         .objects
         .filter(post=OuterRef('post'))
         .order_by('-created')
         .values('pk')
         [:1]
        )
      )
    )
    
    Post.objects.prefetch_related(Prefetch('comment_set', queryset=q)).all()
    

    不幸的是,我们不能分割q 的外部查询,因为 django 不允许这样做。所以我们在子查询内部切片,然后在外部查询上进行过滤。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-03
      • 2011-04-15
      • 2020-03-14
      • 1970-01-01
      • 1970-01-01
      • 2021-10-17
      • 2015-10-29
      • 2018-08-26
      相关资源
      最近更新 更多