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?
CodePudding user response:
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