在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