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

在Django中查询每个用户每个帖子的最新投票,django,postgresql,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,

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

大概是这样的:

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
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  
现在我需要显示20篇帖子,以及用户的最后一次投票

我使用的是
django rest框架
,因此我可以使用如下所示的序列化器字段
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
但这将在每篇文章中执行1次db查询,我希望有更好的解决方案

通过在self.context中保存queryset,我可以在每次运行get_uservote时保存一个db查询,以便涵盖该部分。 但是,如何基于项目列表执行查询,以返回另一个表中的所有
最新
数据呢

首先是
PointLog.objects.filter(user=user,post\uu in=posts)
,但接下来呢?在1查询中使用原始SQL是否可能实现这一点

更新1
PointLog.objects.filter(…).order_by('post_uid').distinct('post_uid')
可以做到这一点,但我认为我不能保证获得最新的
投票。如果我使用order_by('pk')(或'time'),我就不能使用
distinct('post_uid')
,因为我会得到一个sql错误(
ProgrammingError:SELECT distinct ON expressions必须匹配初始order by expressions

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

有效的完整
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\u uservote\u data']
    中,因此我们只需运行一次查询
  • 使用
    i.post\u id
    作为我们数据的键,而不是
    i.post.id
    。在这里请求
    i.post.id
    将触发每个项目的一个查询
  • self.instance
    是包含我们要筛选的所有帖子的查询集
所有这些都导致了一个如下所示的查询:

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
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  

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

有效的完整
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\u uservote\u data']
    中,因此我们只需运行一次查询
  • 使用
    i.post\u id
    作为我们数据的键,而不是
    i.post.id
    。在这里请求
    i.post.id
    将触发每个项目的一个查询
  • self.instance
    是包含我们要筛选的所有帖子的查询集
所有这些都导致了一个如下所示的查询:

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
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