3

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'.

Lord Elrond
  • 13,430
  • 7
  • 40
  • 80
mach2
  • 450
  • 2
  • 6
  • 24
  • Notice also that each Lead can only have one related Channel. So it does not make sense to set `channel = channel_serializer(many=True, ...)` - there **cannot** be **many** channels here. And it is a better idea to make it a CharField, as the provided answers suggest. – natka_m Oct 06 '19 at 15:48

2 Answers2

3

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).

Lord Elrond
  • 13,430
  • 7
  • 40
  • 80
  • 1
    You've got a typo in the `Lead` model, in the `channel` field: the related model name should be `Channel` now. It's only one character, and edits have to change at least 6 chars, so I'd have to find something else to nit-pick... Better fix it yourself :) – natka_m Oct 06 '19 at 15:42
1

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

natka_m
  • 1,297
  • 17
  • 22
Mehran
  • 1,264
  • 10
  • 27
  • if i add ```channelName = serializers.CharField(source='channel.name') ``` ? in performance_serializer, it gives me ```The field 'channel' was declared on serializer performance_serializer, but has not been included in the 'fields' option.``` ....... i have added models in question – mach2 Oct 01 '19 at 07:16
  • I've edited the answer. Basically in case of DRF serializers, the fields that you define manually MUST be present in the Meta.fields as well. – Mehran Oct 01 '19 at 07:24
  • now i am getting ```Got KeyError when attempting to get a value for field `channelName` on serializer `performance_serializer`.\nThe serializer field might be named incorrectly and not match any attribute or key on the `dict` instance.\nOriginal exception text was: 'channel'.``` – mach2 Oct 01 '19 at 07:26
  • I've edited the answer accordinv to your model defintions. Hope it helps! – Mehran Oct 01 '19 at 09:23
  • still geting ```Got 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'.``` – mach2 Oct 01 '19 at 09:38