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
}
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')