2

I am making a django app with comments and voting on those comments, in the stackoverflow or reddit style. When selecting the comments, I would like to know both the aggregate vote count, and whether the user has voted on this particular comment. I can use annotate for the aggregate count like so:

video_comments = Comment.objects.filter(video_id=video_id).annotate(vote_sum=Sum('commentvote__value'))

Can I also annotate a subset of the commentvotes? Something like:

.annotate(user_vote=Sum('commentvote__value').filter(commentvote__user == user))

For reference, this is my model:

class Comment(models.Model):
    video_id = models.CharField(max_length=12, db_index=True)
    video_time = models.FloatField()

    comment = models.TextField(max_length=MAX_COMMENT_LENGTH)

    datetime = models.DateTimeField(auto_now_add = True)
    user = models.ForeignKey(User)

class CommentVote(models.Model):
    comment = models.ForeignKey(Comment, db_index=True)
    value = models.IntegerField() # Should be 1 or -1

    datetime = models.DateTimeField(auto_now_add = True)
    user = models.ForeignKey(User, db_index=True)
2
  • 2
    You might want to add unique_together = ('comment', 'user') to CommentVote. Also, db_index on ForeignKeys is not necessary in MySQL and PostgreSQL. Commented Jan 24, 2012 at 10:27
  • Thanks Tomasz! I missed that unique_together. Commented Jan 26, 2012 at 20:59

2 Answers 2

2

According to this, you can filter on a given field before annotating:

Comment.objects.filter(video_id=video_id).filter(commentvote__user=user))\
               .annotate(user_vote=Sum('commentvote__value'))

Unfortunately this narrows the comment set to just those comments which have vote(s) cast by given user. But you can also get the remaining comments:

Comment.objects.filter(video_id=video_id).exclude(commentvote__user=user))

and combine both lists manually.

Sign up to request clarification or add additional context in comments.

2 Comments

Btw this is also now exactly what the OP needs, but I cannot make two annotations on the same related model work simulatnously, if one is "global" and the second one filtered..
Argh, "now" should be "not" in the above comment
0

I think this should give you what you require:

CommentVote.objects.values('comment').filter(comment__video_id=video_id,user=user).annotate(vote_sum=Sum('value')).values_list('comment','vote_sum',)

10 Comments

Could I please know the mistake that I have committed in this answer?
According to me it just doesn't do what you advertise it should do. But if you prove that it works then I'll happily reverse the vote.
Hey Tomasz, I have found a possible mistake. The filter should be placed before the annotate. Is there anymore mistakes?
Your query is now equivalent to: CommentVote.objects.filter(user=user, comment__video_id=video_id).values_list('comment', 'value'), i.e. it filters votes by given user for given video and returns comment_id and value (disguised as vote_sum) for them.
I think (and it works as well) that the annotate would work based on values('comment') for the particular video_id and user. values_list is next in the process. Please correct me, if I am wrong anywhere.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.