简体   繁体   English

如何使用子查询用其他对象注释 Django QuerySet

[英]How to annotate Django QuerySet with other object using Subquery

In Django version 1.11, Subquery expressions were added.在 Django 1.11 版中,添加了子查询表达式 I was hoping to use this feature to select a related model object based on some filters.我希望使用此功能根据某些过滤器选择相关的模型对象。

This is an example from the 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]))

I would like to do the same, but instead of just annotating with the " newest_commenter_email " in this scenario, I'd like the whole Comment object saved to a newest_comment annotation, like so:我也想做同样的事情,但在这种情况下,我希望将整个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]))

However, this results in the following error:但是,这会导致以下错误:

FieldError: Expression contains mixed types. You must set output_field

Is there a way around this?有没有解决的办法?

You ommited values on the subquery, try this: 您在子查询上省略了values ,请尝试以下方法:

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

where 'author' is the "commenter" field on Comment model. 其中'author'是Comment模型中的“commenter”字段。

You can't do it.你不能这样做。 You can only annotate with a basic type values, not with the object.您只能使用基本类型值进行注释,而不能使用对象进行注释。 The basic reason for this is that result of SQL query is a table with all its cells are basic values, not nested tables.这样做的根本原因是 SQL 查询的结果是一个表,所有单元格都是基本值,而不是嵌套表。 In order to do what you want we need SQL to support nested tables and Django to be able to convert nested tables into nested objects.为了做你想做的事情,我们需要 SQL 来支持嵌套表和 Django 能够将嵌套表转换为嵌套对象。 I don't think that's ever going to happen.我认为这永远不会发生。 So, in order to get several fields of related object you should repeat annotation for each field:因此,为了获得相关对象的多个字段,您应该对每个字段重复注释:

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

Don't be afraid of performance issues due to executing the same query for each field.不要因为对每个字段执行相同的查询而担心性能问题。 SQL engine is smart enough to see that query is the same and to cache its value SQL 引擎足够聪明,可以看到查询是否相同并缓存其值

As suggested by Brobin -正如布罗宾所建议的 -

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

If you really want to go from the Post model though do this -如果你真的想从 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()

Unfortunately we cannot slice in the outer query for q because django disallows it.不幸的是,我们不能在q的外部查询中进行切片,因为 django 不允许这样做。 So we slice inside a subquery and then filter on the outer query.所以我们在子查询中切片,然后过滤外部查询。

You want to filter of the related model, hence this case I feed Prefetch() is one of the best options I can think about您想过滤相关模型,因此我提供Prefetch()的这种情况是我能想到的最佳选择之一

You can read about it from here Django1.1 Prefetch Objects你可以从这里阅读Django1.1 Prefetch Objects

and this is an example of what you're looking for这是你正在寻找的一个例子

from django.db.models import Prefetch
newest = Comment.objects.filter(do_your_filters_here)   
Post.objects.prefetch_related(Prefetch("comments", queryset=newest, to_attr="newest_comments"))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM