how to write serializer for joined model?

Viewed 359

i have applied join on two tables with following query,

VIEWS.PY

class performance(viewsets.ModelViewSet):

    queryset = Leads.objects.select_related('channelId'
        ).values("channelId__channelName").annotate(tcount=Count('channelId'))

    serializer_class = teamwise_lead_performance_serializer

but i am unable to catch response using this serializers,

SERIALIZER.PY

class channel_serializer(serializers.ModelSerializer):
    class Meta:
        model = Channels
        fields = ['channelName']


class performance_serializer(serializers.ModelSerializer):
    tcount = serializers.IntegerField()
    channel = channel_serializer(many=True, read_only=True)

    class Meta:
        model = Leads
        fields = ['tcount', 'channel']

actual results:

[
    {
        "tcount": 88
    },
    {
        "tcount": 25
    },
    {
        "tcount": 31
    },
    ...
]

expected results:

[
    {
        "channelName": "abc",
        "tcount": 88
    },
    {
        "channelName": "def",
        "tcount": 25
    },
    {
        "channelName": "ghi",
        "tcount": 31
    },
    ...
]

i have tried the following:

How to join two models in django-rest-framework

Models.py

class Channels(models.Model):
    id = models.IntegerField(primary_key=True)
    channelName = models.CharField(max_length=20, default=None)

    class Meta:
        db_table = "table1"

class Leads(models.Model):
    id = models.IntegerField(primary_key=True)
    channelId = models.ForeignKey(Channels, on_delete=models.CASCADE, db_column='channelId')

    class Meta:
        db_table = "table2"

why is it not getting the channelName in response? what am i doing wrong here? Thank you for your suggestions

Edit

When I try Mehren's answer, I get the following error:

KeyError when attempting to get a value for field channelName on serializer performance_serializer. The serializer field might be named incorrectly and not match any attribute or key on the dict instance.Original exception text was: 'channelId'.

2 Answers

I managed to get it working with the following:

class performance_serializer(serializers.ModelSerializer):
    tcount = serializers.IntegerField()
    channelName = serializers.CharField(source='channelId__channelName')

    class Meta:
        model = Leads
        fields = ['tcount', 'channelName']

class performance(viewsets.ModelViewSet):
    queryset = Leads.objects.select_related('channelId'
        ).values("channelId__channelName").annotate(tcount=Count('channelId'))
    serializer_class = performance_serializer

That being said, I would strongly encourage you to follow both PEP and Django naming conventions.

Here is what your code would look like following said conventions:

class Channel(models.Model):
    id = models.IntegerField(primary_key=True)
    channel_name = models.CharField(max_length=20, default=None)

class Lead(models.Model):
    id = models.IntegerField(primary_key=True)
    channel = models.ForeignKey(Channel, on_delete=models.CASCADE)

class PerformanceSerializer(serializers.ModelSerializer):
    channel_count = serializers.IntegerField()
    channel_name = serializers.CharField(source='channel__channel_name')

    class Meta:
        model = Lead
        fields = ['channel_count', 'channel_name']

class PerformanceViewSet(viewsets.ModelViewSet):
    queryset = Lead.objects.select_related('channel'
        ).values("channel__channel_name").annotate(channel_count=Count('channel'))
    serializer_class = PerformanceSerializer

The main takeaway from this is to not change the default name of your ForeignKey columns! It makes working with related models much more confusing, and is possibly the reason for your problem in the first place (although I couldn't prove it).

If you want to only get the channelName, then it's better to use

channelName = serializers.CharField(source='channelId.channelName') 

Also, please fix your syntax. You are not following the pep8 standards.

EDIT

class PerformanceSerializer(serializers.ModelSerializer):
    tcount = serializers.IntegerField()
    channelName = serializers.CharField(source='channelId.channelName') 

    class Meta:
        model = Leads
        fields = ['tcount', 'channelName']

EDIT

queryset = Leads.objects.select_related('channelId').values("channelId__channelName").annotate(tcount=Count('channelId'))

Remove the .values("channelId__channelName") part from your view

Related