How do I include related model fields using Django Rest Framework?
Question:
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?
Answers:
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.
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",)
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!
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",)
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?
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.
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",)
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!
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",)