198

Let's say that we have the following model:

class Classroom(models.Model):
    room_number = [...]

class Teacher(models.Model):
    name = [...]
    tenure = [...]
    classroom = models.ForeignKey(Classroom)

Let's say that instead of getting a result like this per the ManyRelatedPrimaryKeyField function:

{
    "room_number": "42", 
    "teachers": [
        27, 
        24, 
        7
    ]
},

have it return something that includes the full related model representation like:

{
    "room_number": "42", 
    "teachers": [
        {
           'id': 27,
           'name': 'John',
           'tenure': True
        }, 
        {
           'id': 24,
           'name': 'Sally',
           'tenure': False
        }, 
    ]
},

Is this possible? If so, how? And is this a bad idea?

4 Answers 4

296

The simplest way is to use the depth argument

class ClassroomSerializer(serializers.ModelSerializer):
    class Meta:
        model = Classroom
        depth = 1

However, that will only include relationships for forward relationships, which in this case isn't quite what you need, since the teachers field is a reverse relationship.

If you've got more complex requirements (eg. include reverse relationships, nest some fields, but not others, or nest only a specific subset of fields) you can nest serializers, eg...

class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        fields = ('id', 'name', 'tenure')

class ClassroomSerializer(serializers.ModelSerializer):
    teachers = TeacherSerializer(source='teacher_set')

    class Meta:
        model = Classroom

Note that we use the source argument on the serializer field to specify the attribute to use as the source of the field. We could drop the source argument by instead making sure the teachers attribute exists by using the related_name option on your Teacher model, ie. classroom = models.ForeignKey(Classroom, related_name='teachers')

One thing to keep in mind is that nested serializers do not currently support write operations. For writable representations, you should use regular flat representations, such as pk or hyperlinking.

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

15 Comments

@Chaz Updated the answer to explain why depth wouldn't do what you need in this case, and to explain the exception you're seeing and how to deal with it.
I'm an idiot and was hitting the wrong server. It definitely works across many to many relationships.
Nesting serializers is awesome! I had to do this and was using DRF 3.1.0. I had to include many=True like so ...TeacherSerializer(source='teacher_set', many=True). Otherwise I was getting the following error: The serializer field might be named incorrectly and not match any attribute or key on the 'RelatedManager' instance. Original exception text was: 'RelatedManager' object has no attribute 'type'.
Hi @TomChristie, thanks for this answer. I'm struggling with this problem myself but one thing I don't understand is where the _set came from in teachers = TeacherSerializer(source='teacher_set'). I tried searching the DRF docs and came up with nothing. Any hints?
The reverse side of a ForeignKey will be named ..._set by default. See the Django docs for more details: docs.djangoproject.com/en/1.10/ref/models/relations/…
|
55

Thank you @TomChristie!!! You helped me a lot! I would like to update that a little (because of a mistake I ran into)

class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        fields = ('id', 'name', 'tenure')

class ClassroomSerializer(serializers.ModelSerializer):
    teachers = TeacherSerializer(source='teacher_set', many=True)

    class Meta:
        model = Classroom
        field = ("teachers",)

2 Comments

What do you do if you want to filter, teachers_set
You can add a list_serializer_class in TeacherSerializer, and add filtering logic by overriding the to_representation function of the list_serializer_class.
8

This can also be accomplished by using a pretty handy dandy django packaged called drf-flex-fields. We use it and it's pretty awesome. You just install it pip install drf-flex-fields, pass it through your serializer, add expandable_fields and bingo (example below). It also allows you to specify deep nested relationships by using dot notation.

from rest_flex_fields import FlexFieldsModelSerializer

class ClassroomSerializer(FlexFieldsModelSerializer):
    class Meta:
        model = Model
        fields = ("teacher_set",)
        expandable_fields = {"teacher_set": (TeacherSerializer, {"source": "teacher_set"})}

Then you add ?expand=teacher_set to your URL and it returns an expanded response. Hope this helps someone, someday. Cheers!

Comments

2

Thanks to @TomChristie and @Paolo

I would just like to add a clarification, the below code works fine but the person has to remember to add the related_name="teacher_set" attribute to the Model Teacher. In this case here is the complete code :

models.py

class Classroom(models.Model):
    room_number = [...]

class Teacher(models.Model):
    name = [...]
    tenure = [...]
    classroom = models.ForeignKey(Classroom, related_name='teacher_set')

Note: related_name='teacher_set' to the classroom field.

serializers.py

class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        fields = ('id', 'name', 'tenure')

class ClassroomSerializer(serializers.ModelSerializer):
    teachers = TeacherSerializer(source='teacher_set', many=True)

    class Meta:
        model = Classroom
        field = ("teachers",)

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.