Django,prefetch_related() 的通用版本?
Django, general version of prefetch_related()?
当然,我不想做 prefetch_related 已经做过的事情。
我想模仿它的作用。
我想做的是以下内容。
我有一个 MyModel 实例列表。
用户可以 follows
或 doesn'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 个单独的查询。
另一个方面是,我想附加查询集而不是附加实际值,这样我就可以将评估推迟到分页。
如果那句话太模棱两可,我想将附加了 is_following
信息的 my_models
查询集提供给另一个函数(例如 DRF 序列化程序)。
prefetch_related
是如何完成上面的事情的?
不确定这是否是最好的方法,我怀疑这是否是 prefetch_related 所做的,因为我要加入这里。
我发现可以在您的查询中找到 select 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
并在查询集中查找属性。
通过 .extra
.
的子查询可以实现只能获得 is_following
位的解决方案
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
个对象的完整列表。
因为您在 Follow
class 中有一个 GFK
,您需要通过 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
属性 - 每个 mymodel
包含所有 follow
个对象的列表,或者您可以使用类似 get_first_following_object
.[=25 的方法=]
当然,我不想做 prefetch_related 已经做过的事情。
我想模仿它的作用。
我想做的是以下内容。
我有一个 MyModel 实例列表。
用户可以 follows
或 doesn'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 个单独的查询。
另一个方面是,我想附加查询集而不是附加实际值,这样我就可以将评估推迟到分页。
如果那句话太模棱两可,我想将附加了 is_following
信息的 my_models
查询集提供给另一个函数(例如 DRF 序列化程序)。
prefetch_related
是如何完成上面的事情的?
不确定这是否是最好的方法,我怀疑这是否是 prefetch_related 所做的,因为我要加入这里。
我发现可以在您的查询中找到 select 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
并在查询集中查找属性。
通过 .extra
.
is_following
位的解决方案
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
个对象的完整列表。
因为您在 Follow
class 中有一个 GFK
,您需要通过 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
属性 - 每个 mymodel
包含所有 follow
个对象的列表,或者您可以使用类似 get_first_following_object
.[=25 的方法=]