查询Django中每个帖子的每个用户的最新投票

时间:2016-01-18 20:40:35

标签: django postgresql

我有一个投票系统,用户可以在帖子上投票或投票。投票将用于计算,因此我需要以日志格式存储它们,即我将每个投票保存在自己的表中。

这样的事情:

class PointLog(models.Model):
    post = models.ForeignKey(Post, db_index=True)
    points = models.IntegerField(db_index=True)
    user = models.ForeignKey(User, blank=True, null=True)
    time = models.DateTimeField(auto_now_add=True, db_index=True)
    data = models.IntegerField()  # -1, 0 or 1

现在我需要显示20个帖子,以及用户最后一次投票。

我正在使用django-rest-framework,所以我可以使用看起来像这样的序列化器字段; uservote = serializers.SerializerMethodField(),以及类似的功能:

def get_uservote(self, obj):
    user = self.context['request'].user
    vote = PointLog.objects.only('data').filter(user=user, post=obj).last()
    return vote.data if vote else 0

但是每个帖子会做一次db-query,我希望有更好的解决方案。

我可以通过将查询集保存在self.context中来保存每次运行get_uservote时的db-query,以便覆盖该部分。 但是,如何根据项目列表进行查询,返回另一个表中的所有latest数据。

一个开始是PointLog.objects.filter(user=user, post__in=posts),但下一步是什么?甚至可以在1个查询中使用原始SQL吗?

更新1 PointLog.objects.filter(...).order_by('post__id').distinct('post__id')会有点做,除非我不认为我能保证获得newest投票。如果我使用order_by(' pk')(或' time'),我无法使用distinct('post__id'),因为我会收到sql错误({{1} })

1 个答案:

答案 0 :(得分:0)

我找到了一个只添加了1个额外查询的解决方案。

有效的完整get_uservote;

def get_uservote(self, obj):
    user = self.context['request'].user
    if user.is_authenticated():
        if not self.context.get('get_uservote_data'):
            self.context['get_uservote_data'] = {i.post_id: i.data for i in PointLog.objects.filter(user=user, post__in=self.instance).order_by('post__id', '-pk').distinct('post__id')}
        return self.context['get_uservote_data'].get(obj.id, 0)
    else:
        return 0  # Return 0 as "not voted" if not logged in

请注意,我们在这里做了几个技巧;

  • 将数据存储在self.context['get_uservote_data']中,这样我们只需运行一次查询。
  • 使用i.post_id作为我们数据的关键,而不是i.post.id。在这里要求i.post.id将触发每个项目1个查询。
  • self.instance是包含我们要过滤的所有帖子的查询集。

这导致了一个如下所示的查询:

SELECT DISTINCT ON ("post_pointlog"."post_id") "post_pointlog"."id",
                                               "post_pointlog"."post_id",
                                               "post_pointlog"."data"
FROM   "post_pointlog"
WHERE  ( "post_pointlog"."user_id" = 20
         AND "post_pointlog"."post_id" IN ( 1, 2, 3, 4 ) )
ORDER  BY "post_pointlog"."post_id" ASC,
          "post_pointlog"."id" DESC