Django使用单个查询检索额外信息

时间:2015-05-21 17:26:48

标签: python django django-orm

我对这些模型有疑问:

class Post(models.Model):
    #Some fields ....


class LikePost(models.Model):
    # Author info
    author = models.ForeignKey(User,related_name='post_likes')
    post = models.ForeignKey(Post, related_name="likes")
    dateJoined = models.DateTimeField(auto_now_add=True)

当我检索帖子的信息时,我还需要知道当前用户(request.user)是否已经在当前帖子上做了likeAction。

当我查看帖子的详细信息时,我会使用单个查询有效地(如果可能)这样做。

目前我使用此查询:

Post.objects.annotate(likes_count=Count('likes')).get(pk=pk)

我使用.annotate()来检索喜欢的数量......

我可以添加关于.annotate().extra()的内容,以了解用户是否已在当前帖子上制作了likeAction吗?

2 个答案:

答案 0 :(得分:0)

这可能是个问题。

根据我的经验,Django ORM最多会将任何特定的表连接到查询中一次。这对于将多个条件应用于相同的相关数据非常有用(这通常非常有用)。你需要的是一些相关数据的两个独立连接;一个你计算所有喜欢的东西,另一个你只关心特定用户喜欢的东西。 Django会做的只是计算特定用户的喜欢,如果你做了像

这样的事情
Posts.objects.filter(likes__user_id=user.id).annotate(
    likes_count=Count('likes')).get(pk=pk)

如果用户不喜欢特定DoesNotExist,则会冒Post例外的风险。如果用户确实喜欢特定帖子likes_count,则只计算特定用户与特定帖子之间的类似关系的数量。我相信这不是你需要的。

但是,您可以在一个查询中完成所有这些操作,但是您将加载比其他要求更多的数据;

post = Posts.objects.select_related('likes').get(pk=pk)
post.likes_count = len(post.likes)
user_already_likes_this = any(like.user_id == user.id for like in post.likes)

现在,您必须对额外数据方法和两种查询方法进行基准测试。

我确信您应该只使用两种查询方法,因为每个http请求可能已经导致15个或更多查询,因此一个额外的不会造成太大的伤害,并且随着喜欢的数量的增加它会更好地扩展增长。

答案 1 :(得分:0)

result = (Post.objects.annotate(likes_count=Count('likes'))
    .extra(
        select={
            'already_liked': (
                "select count(*) from {app_name}_likepost "
                "where {app_name}_likepost.author_id = {uid} "
                "and {app_name}_likepost.post_id = {pid}"
            ).format(
                uid=request.user.id,
                pid=pk,
                app_name=your_django_app_name,
            ),
        }
    )
    .get(pk=pk)
)
result.already_liked # 1 if action performed, 0 if not

但是,这将是子查询,并且某些数据库可能不支持此。 同样django docs建议尽可能避免这种做法。虽然要在一个查询中获取所有需要的数据,但您需要这样做(或手动构建完整的SQL查询)。

正如django docs所述,select_related不会在后向foreign_key关系上工作,而prefetch_related实际上会再做一次查询,所以总共会有两个查询(除此之外)那 - 它会加载更多不需要的数据,就像所有LikePost记录一样。)