Django Model ListAPIView serializer for counting objects

725 views Asked by At

I have a model as follows, Entity :

class Entity(models.Model):
    uuid          = models.CharField(max_length=12, default=None)
    description   = models.CharField(max_length=255, default="")

I want to provide a serialization for the all entity objects where the response will provide the count of each description type that is available in the database.

For instance, the table has the following content:

1.cat 2.dog 3.cat 4.dog 5.bird 6.bird 7.dog

The serialization will be :

dog : 3 cat : 2 bird :2

How should I modify the following serializer code to make this happen ?

#Entity Count(per Intelligence) Search
class EntityFilterSerializer(serializers.ModelSerializer):

    class Meta:
        model = Entity
        fields = ('description')

class StandardResultsSetPagination(PageNumberPagination):
    page_size = 10
    page_size_query_param = 'page_size'
    max_page_size = 100

class EntityList(generics.ListAPIView):
    model = Entity
    serializer_class = EntityFilterSerializer
    filter_backends = [filters.SearchFilter]
    queryset = Entity.objects.order_by('id')
    search_fields = ['=uuid', ]
    pagination_class = StandardResultsSetPagination
1

There are 1 answers

4
Alexandr Tatarinov On BEST ANSWER

The problem is more about getting the data than serializing it. Since keys in your example response are not fixed, creating a Serializer would be problematic and actually not required. You probably do not need pagination for this view. Anyway, that's how I would have implemented it:

 class EntityList(generics.ListAPIView):
    model = Entity
    filter_backends = [filters.SearchFilter]
    search_fields = ['uuid']

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        description_counts = queryset.order_by().values('description').annotate(
            count=Count('*')
        )
        return Response({
            d['description']: d['count']
            for d in description_counts
        })

It will break swagger if you are using one, if that's the case you would have to provide "some" serializer_class to fix it. I prefer to use this beauty. You can write the description of an actual response in the docstring.

class EmptySerializer(serializers.Serializer):
    pass

Alternatively, you can decide to alter the format of the response, and it can be actually preferred by your frontend developers.

[{"description": "cat", "count": 5}, {"description": "dog", "count": 2}]

For this format, you can easily describe a serializer

class CountsSerializer(serializers.Serializer):
    description = serializers.CharField()
    count = serializers.IntegerField()

class EntityList(generics.ListAPIView):
    model = Entity
    filter_backends = [filters.SearchFilter]
    serializer_class = CountsSerializer

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        description_counts = queryset.order_by().values('description').annotate(
            count=Count('*')
        )
        serializer = self.get_serializer(description_counts, many=True)
        return Response(serializer.data)