17

I have a basic setup using the Django Rest Framework. I have two models and a nested serializer setup:

models.py

from django.db import models

class Plan(models.Model):
    name = models.CharField(max_length='100')

    def __unicode__(self):
        return u'%s' % (self.name)

class Group(models.Model):
    plan = models.ForeignKey('plan')
    name = models.CharField(max_length='50')
    weight = models.SmallIntegerField()

    def __unicode__(self):
        return u'%s - %s' % (self.name, self.plan.name)

serializers.py

from plans.models import Plan, Group
from rest_framework import serializers

class GroupSerializer(serializers.ModelSerializer):
    class Meta:
        model = Group
        fields = ('name', 'weight')

class PlanSerializer(serializers.ModelSerializer):
    group = GroupSerializer(many=True, read_only=True)

    class Meta:
        model = Plan
        fields = ('name', 'group')

views.py

from rest_framework import viewsets

from plans.models import Plan
from plans.serializers import PlanSerializer

class PlanViewSet(viewsets.ModelViewSet):
    queryset = Plan.objects.all()
    serializer_class = PlanSerializer

When I view the serializers relationships in Django's Shell it shows the relationship correctly:

PlanSerializer():
name = CharField(max_length='100')
group = GroupSerializer(many=True, read_only=True):
    name = CharField(max_length='50')
    weight = IntegerField()

What I end up getting back via cURL is:

[
    {
        name: Test Plan
    }
]

What I expect to get back is:

[
    {
        name: Test Plan,
        group: [
                {
                    name: Test Group,
                    weight: 1
                }
        ] 
    }
]

There is no nested data coming through. I'm at a lose for what I've not setup correctly here. Can anyone point me in the correct direction?

3 Answers 3

24

The problem comes from your queryset: queryset = Plan.objects.all(). None of the items in this queryset has .group attribute that's why your result is empty. By default Django creates a reverse relation of the plan ForeignKey called group_set (unless you don't rename it via related_name) (this means that every plan item in the queryset have a group_set attribute which is a queryset containing all the groups of this plan). You can use this attribute in order to get a proper serialization. This means to change:

class PlanSerializer(serializers.ModelSerializer):
    group_set = GroupSerializer(many=True, read_only=True)

    class Meta:
        model = Plan
        fields = ('name', 'group_set')

If you really want to stick with group (btw this is a very bad name for a list of groups). You can hack it with prefetch_related like so:

queryset = Plan.objects.prefetch_related('group_set', to_attr='group')

this way every plan item will have a group attribute - a queryset containing all the groups for this plan.

Sign up to request clarification or add additional context in comments.

5 Comments

Appreciate the help. I changed to group_set as I wasn't wed to the name and that fixed it. I'll also look into changing the name from group to categories or something like that.
You can change the default xxx_set notation at the moment when you define the FK via related_name, e.g. plan = models.ForeignKey('plan', related_name='groups'). Now you wont have group_set attribute but groups instead.
This really helped me find the cause of my own issue. I'd suggest adding your last comment Todor to your answer as having proper related_names in your model which the serializer can use seems a much more complete answer.
In my opinión @Todor's approach is the proper one, or at least looks cleaner than the accepted answer.
Thank you, Todor, just little ambiguous about the custom field naming in DRF. I thought I can name the field freely : )
3

Never forget to give related name for the foreign key. for eg

In models

plan = modles.ForeignKey(Plan, related_name="plan")

In Serializers

plan = PlanSerializers(many = True, read_only = True)

Comments

0

Although not directly applicable to the OP, this may help others that end up here based on the title:

DRF nested serializer not showing related data

This problem also arises if you specify an invalid source value.

For example, if you accidentally try to use django filter notation (a__b) when specifying related objects:

class MySerializer(...):
    ...
    my_field = MyOtherSerializer(
        source='my_related_field__some_set', many=True, read_only=True
    )

In DRF 3.15.2, this does not produce an error. Instead it fails silently: my_field simply does not show up in the serialized data.

To fix this, use dotted notation, as mentioned in the docs:

source='my_related_field.some_set'

This does work, but beware: If my_related_field is null, we still get a missing my_field.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.