Nick
Nick

Reputation: 45

DRF Get likes and dislikes grouped by day

I have a models named Post and Like. How can i get json with ount of likes and dislikes grouped by date (date field in Like model)?

Here is my models.py

class Post(models.Model):
    """Post model"""
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    title = models.CharField(max_length=255)
    body = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title


class Like(models.Model):
    """Like model"""
    LIKE = (
        ('like', 'like'),
        ('dislike', 'dislike')
    )

    user = models.ForeignKey(User, on_delete=models.CASCADE)
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='likes')
    like = models.CharField(max_length=255, choices=LIKE)
    date = models.DateField(auto_now=True)

Here is my serializers.py:

class AnaliticsSerializer(serializers.ModelSerializer):
    """Like analitic"""

    class Meta:
        model = Like
        fields = '__all__'

Here is my vievs.py:

class AnaliticView(ListAPIView):
    queryset = Like.objects.all()
    serializer_class = AnaliticsSerializer
    filter_backends = [DjangoFilterBackend]
    filter_fields = ['date']

result what i want

[
  {
    "date": "2020-11-14",
    "total_likes": 25,
    "total_dislikes": 17
  },
  {
    "date": "2020-11-15",
    "total_likes": 38,
    "total_dislikes": 8
  },
  {
    "date": "2020-11-18",
    "total_likes": 11,
    "total_dislikes": 0
  }

Upvotes: 0

Views: 854

Answers (1)

Simon Crowe
Simon Crowe

Reputation: 468

Here's a working example of one basic approach to this. It should give you the idea.

The analytics code shouldn't really be in the view. Also, some of the grouping and counting might be offloaded to the database, using advanced ORM querying like this.

views.py

from collections import Counter
from datetime import datetime, timedelta
from itertools import groupby

from django_filters import rest_framework as filters
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response

from likes.filters import DateRangeFilterSet
from likes.models import Like


class AnaliticView(GenericAPIView):
    queryset = Like.objects.all()
    filter_backends = (filters.DjangoFilterBackend,)
    filterset_class = DateRangeFilterSet

    def get(self, request, format=None):
        queryset = self.get_queryset()
        filtered_queryset = self.filter_queryset(queryset)

        # Queryset needs to be ordered by date for groupby to work correctly
        ordered_queryset = filtered_queryset.order_by('date')
        likes_by_date = groupby(ordered_queryset,
                                lambda like: like.date.strftime("%Y-%m-%d"))

        analytics = []
        for date, likes in likes_by_date:
            count = Counter(like.like for like in likes)
            analytics.append(
                {
                    'date': date,
                    'total_likes': count['like'],
                    'total_dislikes': count['dislike'],

                }
            )

        return Response(analytics)                                  

Like I said in the comments, it would be possible to create a lightweight class with attributes for date and the two totals, and pass a list of instances of that to a serializer to get the response data. In my opinion, that's overkill as you can just build a dictionary that is easily serialised into JSON.

Update:

I've switched to a GenericAPIView, which is a superclass of ListAPIView, because it supports filter backends. I have added a FilterSet that filters by date_from and date_to:

filters.py

from django_filters import rest_framework as filters

from likes import models


class DateRangeFilterSet(filters.FilterSet):
    date_from = filters.DateFilter(field_name='date', lookup_expr='gte')
    date_to = filters.DateFilter(field_name='date', lookup_expr='lte')

    class Meta:
        model = models.Like
        fields = ('date_from', 'date_to')

Upvotes: 1

Related Questions