Django Relational Vote Counts with Rest Framework

Hi guys,

I have in the following models.

class Vote(models.Model):
REACTIONS = (
(0, “up_vote”),
(1, “down_vote”),
(2, “love”)
)
user = models.Foreignkey(User)
article = models.ForeignKey(Article, related_query_name=“vote”, related_name=“votes”)
reaction = models.PositiveIntegerField(choices=REACTIONS)

class Article(models.Model):
title = models.CharField()
content = models.TextField()

And i want to get vote counts with rest framework like this.

	[
	{
		"title": "Example",
		"content": "Test",
		"votes": {
			"up_vote": 3,
			"down_vote": 2,
			"love": 0
		}
	},
           {
	"title": "Example2",
	"content": "Test2",
	"votes": {
		"up_vote": 0,
		"down_vote": 2,
		"love": 1
	}
}
]

I can’t handle it. Can you help me?

Since you’re retrieving multiple articles, what you’re getting is actually a queryset, so you’ll be looking to aggregate values in each row of that queryset, and annotate each row with those values. Those become data elements within the models that can be added to your serializer to be returned to the client.

Start with reviewing the Aggregation documentation, specifically, the last example in the Cheat Sheet,
Following Relationships Backwards and Aggregations and Other Queryset Clauses

Ken

Thank you for your reply Ken.

Actually I already try annotate. But I don’t want to do in the following example.

	up_vote_count = Count("id", filter=Q(vote__reaction=0))
	down_vote_count = Count("id", filter=Q(vote__reaction=1))
	love_vote_count = Count("id", filter=Q(vote__reaction=2))

	Article.objects.annotate(up_vote_count=up_vote_count, down_vote_count=down_vote_count, love_vote_count=love_vote_count)

I wonder if there is a better way?

Why don’t you want to do it this way? Why do you think there is (or should be) a different way of doing it?

I understand the desire to make things look “clean and neat”, but if you look at the query generated by this, you’ll see it’s one query with three count clauses, a join, and a group by - pretty much what I would write if I were writing this as SQL directly.

1 Like

If i will add some new reaction like (3, “hate”) I have to update my query. But i guess can write function which return query like get_articles. It uses REACTIONS variable.

Ok, that’s a different issue. If you’re looking for something to automatically generate in your query that is based on REACTIONS, you could do something like:

counts = {vote_type[1]: Count(“id”, filter=Q(vote__reaction=vote_type[0])) for vote_type in Vote.REACTIONS}
Article.objects.annotate(**counts)

1 Like

Yes Ken. I already did something like this. Thank you for your assist. I appreciate it.