Django,prefetch_related()的一般版本?

时间:2015-03-28 12:58:02

标签: django django-queryset

当然,我并不打算做prefetch_related已经做的事情。

我想模仿它的作用 我想做的是以下内容。

我有一个MyModel实例列表 用户可以followsdoesn't follow每个实例。

my_models = MyModel.objects.filter(**kwargs)

for my_model in my_models:
   my_model.is_following = Follow.objects.filter(user=user, target_id=my_model.id, target_content_type=MY_MODEL_CTYPE)

这里我有n + 1查询问题,我想我可以借用prefetch_related在这里做的事情。 prefetch_related的描述说,它执行所有对象的查询,当需要相关属性时,它从预先执行的查询集中获取。

这正是我所追求的,对我感兴趣的所有对象执行is_following的查询,并使用查询而不是N个人查询。

另外一个方面是,我想附加查询集而不是附加实际值,这样我就可以推迟评估直到分页。
如果这个含糊不清的语句,我想将附有my_models信息的is_following查询集提供给另一个函数(例如DRF序列化程序)。

prefetch_related如何完成上述内容?

2 个答案:

答案 0 :(得分:0)

不确定这是否是最佳方法,我怀疑这是prefetch_related的作用,因为我加入了这里。

我找到了在您的查询中选择extra列的方法。

        extra_select = """
        EXISTS(SELECT * FROM follow_follow
        WHERE follow_follow.target_object_id = myapp_mymodel.id AND
        follow_follow.target_content_type_id = %s AND
        follow_follow.user_id = %s)
        """

        qs = self.extra(
            select={'is_following': extra_select},
            select_params=[CONTENT_TYPE_ID, user.id]
        )

所以你可以通过加入来做到这一点。

prefetch_related这样做的方式是separate queryset,并在查询集中查找属性。

答案 1 :(得分:0)

通过is_following子查询可以获得只能获得.extra位的解决方案。

class MyModelQuerySet(models.QuerySet):
    def annotate_is_follwing(self, user):
        return self.extra(
            select = {'is_following': 'EXISTS( \
                SELECT `id` FROM `follow` \
                WHERE `follow`.`target_id` = `mymodel`.id \
                AND `follow`.`user_id` = %s)' % user.id
            }
        )

class MyModel(models.Model):

    objects = MyModelQuerySet.as_manager()

用法:

my_models = MyModel.objects.filter(**kwargs).annotate_is_follwing(request.user)

现在是另一种解决方案,您可以获得following个对象的完整列表。

由于GFK课程中有Follow,您需要通过GenericRelation手动创建reverse关系。类似的东西:

class MyModelQuerySet(models.QuerySet):
    def with_user_following(self, user):
        return self.prefetch_related(
            Prefetch(
                'following', 
                queryset=Follow.objects.filter(user=user) \
                    .select_related('user'), 
                to_attr='following_user'
            )
        )

class MyModel(models.Model):

    following = GenericRelation(Follow,
        content_type_field='target_content_type',
        object_id_field='target_id'
        related_query_name='mymodels'
    )

    objects = MyModelQuerySet.as_manager()

    def get_first_following_object(self):
        if hasattr(self, 'following_user') and len(self.following_user) > 0:
            return self.following_user[0]
        return None

用法:

my_models = MyModel.objects.filter(**kwargs).with_user_following(request.user)

现在您可以访问following_user属性 - 包含follow个所有mymodel个对象的列表,或者您可以使用get_first_following_object之类的方法。