如何在树状注解结构django中优化SQL?

pw136qt2  于 2023-03-20  发布在  Go
关注(0)|答案(1)|浏览(134)

我有一些帖子里面有树评论。评论也有喜欢的。我已经设法优化了SQL查询,与帖子本身相关的普通评论不会创建新的查询,但对其他评论的回答仍然会创建3-4个新的数据库请求。我能以某种方式减少查询的数量吗?我一直在使用prefetch_related来实现这个目的。
我的帖子模型:

class Post(models.Model):
    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE
    )
    title = models.CharField(
        max_length=200,
        null=True,
        blank=True
    )
    header_image = models.ImageField(
        null=True,
        blank=True,
        upload_to="posts/headers",
        help_text='Post will start from this image'
    )
    body = CharField(
        max_length=500
    )

    post_date = models.DateTimeField(
        auto_now_add=True
    )
    likes = models.ManyToManyField(
        User,
        through='UserPostRel',
        related_name='likes',
        help_text='Likes connected to the post',
    )

    def total_likes(self):
        return self.likes.count()

评论模型

class Comment(models.Model):
    user = models.ForeignKey(User, related_name='comment_author', on_delete=models.CASCADE)
    post = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE)
    body = models.TextField(max_length=255)
    comment_to_reply = models.ForeignKey("self", on_delete=models.CASCADE, null=True, blank=True, related_name='replies')
    likes = models.ManyToManyField(User, through='CommentLikeRelation', related_name='comment_likes')
    created_at = models.DateTimeField(auto_now_add=True)

    def replies_count(self):
        return self.replies.count()

    def total_likes(self):
        return self.likes.count()

    def is_a_leaf(self):
        return self.replies.exists()

    def is_a_reply(self):
        return self.comment_to_reply is not None

class CommentLikeRelation(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    comment = models.ForeignKey(Comment, on_delete=models.CASCADE)

查看流程数据

def get(self, request, *args, **kwargs):
        current_user = request.user
        user_profile = User.objects.get(slug=kwargs.get('slug'))
        is_author = True if current_user == user_profile else False

        comment_form = CommentForm()
        Posts = Post.objects.filter(author=user_profile.id)
            .select_related('author')
            .prefetch_related('likes')
            .prefetch_related(
                'comments',
                'comments__user',
                'comments__likes',
                'comments__comment_to_reply',
                'comments__replies',
                'comments__replies__user',
                'comments__replies__likes',
            )
            .order_by('-post_date')
        )

        return render(request, self.template_name,
                      context={
                          'is_author': is_author,
                          'current_user': current_user,
                          'profile': user_profile,
                          'form': comment_form,
                          'posts': posts,
                      })

aiazj4mn

aiazj4mn1#

您可以使用prefetch_related方法在一个查询中预取相关的评论和回复,沿着它们的作者和喜欢的内容,这是减少查询次数的一种方法。
Django的文档中解释了如何使用它,除此之外,这完全取决于你自己。例如,你可以修改Comment以包含一个related_name用于回复(Django会为你处理),然后使用prefetch_related进行相关的评论和回复,如下所示:

class Comment(models.Model):
    # other fields
    post = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE)
    comment_to_reply = models.ForeignKey("self", on_delete=models.CASCADE, null=True, blank=True, related_name='replies')
    # ...

# you can do it inline too, if you want
comment_prefetch = Prefetch('comments', queryset=Comment.objects.select_related('user').prefetch_related('likes', 'replies__user', 'replies__likes'))

# and then you can query
posts = Post.objects.filter(author=user_profile.id)\
            .select_related('author')\
            .prefetch_related('likes', comment_prefetch)\

这样,您可以在一个查询中获得所有相关的评论和回复,沿着它们的作者和喜欢的内容;Django将使用对象缓存来处理剩下的部分,只需要最少的额外查询。
或者,为了进一步优化,您可以使用select_related在单个查询中获取相关对象,而不是对每个对象进行附加查询;最后,将所需的金额注解为计算值:

comment_prefetch = Prefetch('comments', queryset=Comment.objects.select_related('user').prefetch_related('likes', 'replies__user', 'replies__likes').annotate(nb_likes=Count('likes'), nb_replies=Count('replies'))

# Post.objects.filter is the same as above

相关问题