我有一个投票系统,用户可以在帖子上投票或投票。投票将用于计算,因此我需要以日志格式存储它们,即我将每个投票保存在自己的表中。
这样的事情:
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} })
答案 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