Add annotated value in json response (django rest)?

Viewed 122

I got a model that looks like this:

class Book(models.Model):
    author = models.CharField(max_length=100)
    chapters = models.IntegerField()
    name = models.CharField(max_length=100)

and a "grand child" linked together through a Chapter model:

class Verse(models.Model):
    chapter = models.ForeignKey(Chapter, on_delete=CASCADE)
    verse = models.TextField()
    verse_number = models.IntegerField()

I wish to get the count of all verses that belongs to a book, and I'm fetching them like this:

Book.objects.annotate(Count('chapters', distinct=True), total_num_verses=Count('chapter__verse', distinct=True))

However, I'm not sure how to get this into my serializer. I was thinking of using a SerializerMethodField The goal is to get the total_num_verses as a key/value pair in my json response

class BookSerializer(serializers.ModelSerializer):
    total_verse_count = serializers.SerializerMethodField()

    class Meta:
        model = Book
        fields = "__all__"

    # this doesn't work..
    def get_total_verse_count(self, obj):
        print(obj.get_verses)
        return self.annotate(Count('chapters', distinct=True), total_num_verses=Count('chapter__verse', distinct=True))

I just get 'BookSerializer' object has no attribute 'annotate'

Should I make a @property method in the Book model class itself?

views.py is just a plain APIView

class BookAPIView(APIView):
    """
    List all books
    """

    def get(self, request, format=None):
        books = Book.objects.all()
        serializer = BookSerializer(books, many=True)
        return Response(serializer.data)
1 Answers

An effective way to do that is adding a readonly=True field in the serializer:

class BookSerializer(serializers.ModelSerializer):
    total_verse_count = serializers.IntegerField(read_only=True)

    class Meta:
        model = Book
        fields = ['author', 'chapters', 'name', 'total_verse_count']

In the view, you then pass the annotated queryset to the serializer:

class BookAPIView(APIView):

    def get(self, request, format=None):
        books = Book.objects.annotate(
            total_verse_count=Count('chapter__verse')
        )
        serializer = BookSerializer(books, many=True)
        return Response(serializer.data)
Related