在Django ORM中过滤关系字段

时间:2019-02-12 12:05:19

标签: python django django-orm

我有一个模型媒体,它与模型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做到这一点吗?

1 个答案:

答案 0 :(得分:3)

使用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)                    

请注意,与之前针对主查询集中的每个对象的一个​​附加查询相比,这将导致一个附加查询来预取所有相关对象(因此,总共两个查询,无论您获取多少行)。