[Answered ]-Django DRF: how to groupby on a foreign fields?

1👍

Work with a serializer for the topic:

class TopicSerializer(serializers.ModelSerializer):
    upvotes = serializers.IntegerField(read_only=True)

    class Meta:
        model = Topic
        fields = ['id', 'name', 'upvotes']

then in the ModelViewSet, you annotate:

from django.db.models import Sum
from rest_framework.viewsets import ModelViewSet


class TopicViewSet(ModelViewSet):
    serializer_class = TopicSerializer
    queryset = Topic.objects.annotate(upvotes=Sum('userupvotes__upvotes'))

0👍

Desired output
This is the result I want to get. When I don’t perform any aggregations (and there are views where this will be the case), it works.

[
    {
        "topic_name": 3,
        "topic_name": "Korean Studies",
        "upvotes": 14
    },
    {
        "topic_name": 12,
        "topic_name": "Inflation",
        "upvotes": 3
    },
]

The serialized FK will always give you the ID of the related model. I am not sure why you name it topic_name if that is equal to an ID. Now, if you really want to get the name field of the Topic model
in the topic_name = serializers.StringRelatedField(source="topic") you should give it a source="topic.name"

However, if you trying to get the ID of the relation you can still use ModelSerializer :

class TopicUpvotesSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserUpvotes
        fields = "__all__"

0👍

@willem-van-onsem‘s answer is the correct one for the problem as I had put it.

But… I had another use case (sorry! ◑﹏◐), for when the Users API used UserUpvotes serializer as a nested field. So I had to find another solution. This is was I eventually ended up with. I’m posting in case it helps anyone.

class UserUpvotesSerializer(serializers.ModelSerializer):
topic_name = serializers.SerializerMethodField()

def get_topic_name (self, obj):
    try:
        _topic_name  = obj.topic.name
    except TypeError:
        _topic_name = obj.get("skill__name", None)
    return _topic_name

class Meta:
    model = UserUpvotes
    fields = ["topic_id", "topic_name", "upvotes"]

I still have no idea why the SerializerMethodField works and the StringRelatedField field doesn’t. It feels like a bug?

Anyways, the rub here is that, after the values().annotate() aggregation, obj is no longer a QuerySet, but a dict. So accessing namedirectly will give you a ‘UserUpvotes’ object is not subscriptable error.

I don’t know if there are any other edge cases I should be aware of (this is when I REALLY miss type hints in Django), but it works so far

Leave a comment