Krzysiek Perkowski
Krzysiek Perkowski

Reputation: 3

How to create custom output JSON from two serializers

I'am a newbie in Rest Framework in Django, and I have an exercise. I want to create custom JSON output from two Django models. I've already created two serialized views. The output JSON should be:

{

    "movie_id": 2,

    "total_comments": 4,

    "rank": 1

},

{

    "movie_id": 3,

    "total_comments": 2,

    "rank": 2

},

{

    "movie_id": 4,

    "total_comments": 2,

    "rank": 2

}

And the GET /top url should:

I have two serializers:

class CommentSerializer(serializers.ModelSerializer):
   class Meta:
      model= Comment
      fields=('movie','user','timestamp','content','approved')
class MovieSerializer(DynamicFieldsModelSerializer,serializers.HyperlinkedModelSerializer):
     comments=CommentSerializer(many=True)
     comments=None

    class Meta:
        model = Movie

        fields = ('id','name','description','year','released','comments','rating')

This is Comment model:

class Comment(models.Model):
     movie = models.ForeignKey(Movie, on_delete=models.DO_NOTHING,related_name='comments',blank=True,null=True)
    content = models.TextField()
    timestamp = models.DateTimeField(default=timezone.now)
    user=models.CharField(max_length=250)

I tried to do that on my own by this trick:

class MovieViewSet(viewsets.ModelViewSet):

      queryset = Movie.objects.all()
      serializer_class = MovieSerializer

class CommentViewSet(viewsets.ModelViewSet):
      queryset = Comment.objects.all()
      serializer_class = CommentSerializer
class TopViewSet(viewsets.ModelViewSet):
      serializer_class = MovieSerializer
      #queryset = Movie.objects.annotate(comment_count=(Count('comments'))).order_by("-comment_count","-rating")
       queryset = Movie.objects.all().annotate(comment_count=(Count('comments'))).order_by("-comment_count")

But I have no idea how to create customized JSON output

If someone could help me also in the last point, i.e. date, I would be grateful.

Upvotes: 0

Views: 173

Answers (2)

ruddra
ruddra

Reputation: 51988

I think you can do it like this:

# serializer
class MovieSerializer(HyperlinkedModelSerializer):
     comments = CommentSerializer(many=True)
     comments_count = serializer.IntegerField()
     rank = serializer.IntegerField()

    class Meta:
        model = Movie
        fields = ('id','name','description','year','released','comments','rating', 'comments_count', 'rank')

# Viewset

from django.db.models import Sum, F
from django.db.models.expressions import Window
from django.db.models.functions import Rank


class TopViewSet(viewsets.ModelViewSet):
      serializer_class = MovieSerializer
      queryset = Movie.objects.all()

       def get_queryset(self, *args, **kwargs):
          from_date = self.request.query_params.get('from_date')
          to_date = self.request.query_params.get('to_date')
          movies = super(ToViewSet, self).get_queryset()
          if from_date and to_date:
               movies = movies.filter(release__range=[from_date, to_date])
          return movies.annotate(comment_count=Count('comments'), rank=Window(expression=Rank(), order_by=F('comments').desc()),)

Explanation:

In get_queryset() method, I have use self.request.query_params.get(...). This is used to get URL Querystring. So when you call /your_url/?from_date=2018-01-01&to_date=2019-01-31, it will filter queryset between the given date range.

Also, I have used Window Function of Django model to annotate rank of the objects.

Finally, in the serializer I have added 2 extra fields in Serializer. It will capture the annotated values from the queryset and display as output.

Upvotes: 1

Yaser Rahimi
Yaser Rahimi

Reputation: 73

i think you should rewrite get method in your view:

from rest_framework.response import Response
from rest_framework import status





class MovieViewSet(viewsets.ModelViewSet):

    queryset = Movie.objects.all()
    serializer_class = MovieSerializer
    def get(self):
        """ do some thing"""
        output = {{    
                    "movie_id": 2,
                    "total_comments": 4,
                    "rank": 1
                   },
                   {
                    "movie_id": 3,
                    "total_comments": 2,
                    "rank": 2
                   },
                   {
                    "movie_id": 4,
                    "total_comments": 2,
                    "rank": 2
                    }
                   }
        return Response(output, status=status.HTTP_200_OK)

for more detailes read DRF doc: https://www.django-rest-framework.org/api-guide/generic-views/#generic-views

Upvotes: 0

Related Questions