'How to add field to Django queryset

I'm learning DRF and trying to make website, where users can leave reviews for some products and also rate (like or dislike) this reviews. So now I'm stuck in like system. For example I have these models:

class Review(models.Model):
    author = models.ForeignKey(User)
    text = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

class Reaction(models.Model):
    author = models.ForeignKey(User)
    review = models.ForeignKey(Review, related_name='reactions')
    like = models.BooleanField()  # True = like, False = dislike

And I want to show list of posts with like and dislike counters and also somehow mark that logged user already liked or disliked a post. I made view to list posts with counters, but don't know how to include information about post was rated by user.

class ListReviews(generics.ListAPIView):
    serializer_class = ProductReviewSerializer

    def get_queryset(self):
        product_slug = self.kwargs['product_slug']
        queryset = Review.objects.filter(product__slug=product_slug).annotate(
                likes_count=Count('reactions', filter=Q(reactions__like=True)),
                dislikes_count=Count('reactions', filter=Q(reactions__like=False)),
                user_reaction=...
                )
        return queryset

Is it possible at all? Or it's better to just make another endpoint to get user reactions for current page?



Solution 1:[1]

This can be done using a Subquery that selects Reviews filtered by the current user and use that as an annotation

# You'll need these imports
from django.db.models import Q, Count, OuterRef, Subquery

def get_queryset(self):
    product_slug = self.kwargs['product_slug']
    user_reactions = Reaction.objects.filter(review=OuterRef('pk'), author=self.request.user)
    queryset = Review.objects.filter(product__slug=product_slug).annotate(
        likes_count=Count('reactions', filter=Q(reactions__like=True)),
        dislikes_count=Count('reactions', filter=Q(reactions__like=False)),
        user_reaction=Subquery(user_reactions.values('like')[:1])
    )
    return queryset

The user_reaction annotation will then be True, False or None when the user liked a review, disliked a review or hasn't reacted respectively

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Iain Shelvington