【问题标题】:Filter on relation field in Django ORM在 Django ORM 中过滤关系字段
【发布时间】:2019-07-06 01:56:54
【问题描述】:

我有一个模型媒体,它与模型 UserMedia(用户评级)有关系。还有一个与问题相关的称为 UserMatchScore(用户匹配分数)的模型。

在我正在查询媒体表的视图中,在此视图中,有一个选项可以仅获取我的比赛已评分但我尚未评分的媒体。此外,还会根据我和我的匹配项的子集返回平均评分,而不是所有对媒体进行评分的用户。我用注释来做到这一点。

我所做的是过滤媒体表中我未评级但我的匹配已评级的元素,这很简单,但它只完成了一半的工作。返回所有我未评级的媒体,但我的匹配已评级,但关系字段 UserMedia 仍包含所有评级,此未过滤,因此无法计算我的匹配子集的平均评级. 这是我正在描述的查询:

queryset = models.Media.objects
queryset = queryset.filter(
               Q(usermedia__user__id__in=my_matches) & ~Q(usermedia__user=user)
            )

获得预期结果的唯一方法是遍历查询集并过滤 UserMedia 关系的每个元素,但这太慢了,因此必须使用数据库查询来完成。

for el in queryset:                    
    el.usermedia_set.filter(~Q(user=user)).filter(user=my_matches)

有人知道如何用 Django ORM 做到这一点吗?

【问题讨论】:

  • 为什么在所有查询中都使用 Q 对象?在这些例子中它们不是没用吗?
  • 继续@Lotram 评论 - 条件.filter(usermedia__user_id__in=my_matches) 不够,或者用户可以参加自己的比赛吗?关于这个问题,因为它对我来说并不完全清楚 - 你的问题是如何用用户匹配的平均评分来注释每个 Media
  • ~Q() 用于不包括,Q() 可以省略。
  • 在第二个查询中,您可以使用.exclude() 而不是.filter(~Q)
  • 你试过this吗?像 Media.objects.filter(usermedia__user_id__in=my_matches).exclude(user=user).annotate(avg_rating=Avg('usermdia__rating', filter=Q(usermedia__user_id__in=mymatches)) 不确定它是否有效,但 filter 关键字似乎旨在做到这一点。

标签: python django django-orm


【解决方案1】:

使用Prefetch,您可以使用预过滤的查询集获取关系的相关对象(对于多对多和反向外键关系):

queryset = queryset.filter(
               Q(usermedia__user__id__in=my_matches) & ~Q(usermedia__user=user)
            )

prefetch = UserMedia.objects.filter(user_id__in=my_matches).exclude(user=user)
queryset = queryset.prefetch_related(
    Prefetch('usermedia_set', prefetch, to_attr='filtered_usermedia')
)

for el in queryset:
    for usermedia in el.filtered_usermedia:
        # iterate over the filtered usermedia
        # without any additional queries
        calculate_something(usermedia)                    

请注意,与之前对主查询集中的每个对象的额外查询相比,这将导致一个额外的查询用于预取所有相关对象(因此总共有两个查询,无论您提取多少行)。

【讨论】:

  • 很好的答案,只有一个缺点,预取将“filtered_usermedia”放入列表,而不是查询集。而且我需要能够在这个子集上运行 Avg,而不能在列表上运行它。当然我想避免for循环,因为性能非常低。
  • 我看到我可以使用 to_attr 参数,所以这是我提供的做我需要的方法。非常感谢!
猜你喜欢
  • 2015-04-30
  • 2017-07-02
  • 1970-01-01
  • 2023-03-06
  • 2020-08-28
  • 2018-06-19
  • 2021-10-23
  • 2015-05-04
  • 2020-10-23
相关资源
最近更新 更多