What I need
I’d like to get a single json field in the API response which lists identifiers for related objects based on two reverse foreign key relationships. Simple example what I mean by that is presented below. I’d highly prefer it to be handled on the Django REST Framework serializer level rather than having to change the model in some way, but I have very little DRF experience and I can’t for the life of me figure out how to actually do it.
Example models.py:
class Person(models.Model):
id = models.AutoField(primary_key=True)
first_name = models.CharField(max_length=50, blank=True, null=True)
last_name = models.CharField(max_length=50, blank=True, null=True)
father = models.ForeignKey(
"self",
related_name="children_as_father",
blank=True,
null=True,
on_delete=models.SET_NULL,
)
mother = models.ForeignKey(
"self",
related_name="children_as_mother",
blank=True,
null=True,
on_delete=models.SET_NULL,
)
Example database data:
id | first_name | last_name | mother | father |
---|---|---|---|---|
1 | Jane | Smith | ||
2 | John | Smith | ||
3 | Clarence | Smith | 1 | 2 |
4 | Thomas | Smith | 1 | 2 |
Example serialized json I would like to get:
[
{
"pk": 1,
"first_name": "Jane",
"last_name": "Smith",
"mother": null,
"father": null,
"children": [
3,4
],
},
{
"pk": 2,
"first_name": "John",
"last_name": "Smith",
"mother": null,
"father": null,
"children": [
3,4
],
},
{
"pk": 3,
"first_name": "Clarence",
"last_name": "Smith",
"mother": 1,
"father": 2,
"children": [],
},
{
"pk": 4,
"first_name": "Thomas",
"last_name": "Smith",
"mother": 1,
"father": 2,
"children": [],
}
]
What I tried
That’s as far as my experiments managed to get:
serializers.py
from rest_framework.serializers import ModelSerializer, PrimaryKeyRelatedField
from .models import Person
class FamilySerializer(ModelSerializer):
children_as_mother = PrimaryKeyRelatedField(
many=True, read_only=True, allow_null=True
)
children_as_father = PrimaryKeyRelatedField(
many=True, read_only=True, allow_null=True
)
class Meta:
model = Person
fields = [
"pk",
"first_name",
"last_name",
"mother",
"father",
"children_as_mother",
"children_as_father",
]
views.py
from rest_framework.permissions import IsAuthenticated
from rest_framework.viewsets import ModelViewSet
from .models import Person
from .serializers import FamilySerializer
class FamilyViewSet(ModelViewSet):
"""
API endpoint with family data
"""
queryset = Person.objects.all()
serializer_class = FamilySerializer
permission_classes = [IsAuthenticated]
Serialized json:
[
{
"pk": 1,
"first_name": "Jane",
"last_name": "Smith",
"mother": null,
"father": null,
"children_as_mother": [
3,4
],
"children_as_father": [],
},
{
"pk": 2,
"first_name": "John",
"last_name": "Smith",
"mother": null,
"father": null,
"children_as_mother": [],
"children_as_father": [
3,4
],
},
{
"pk": 3,
"first_name": "Clarence",
"last_name": "Smith",
"mother": 1,
"father": 2,
"children_as_mother": [],
"children_as_father": [],
},
{
"pk": 4,
"first_name": "Thomas",
"last_name": "Smith",
"mother": 1,
"father": 2,
"children_as_mother": [],
"children_as_father": [],
}
]
As you can see compared to the first json example, it’s not quite what I want – I’d like a single json field for listing a person’s children identifiers ("children") and not two ("children_as_mother", "children_as_father").
2
Answers
I managed to find an acceptable solution. Here's how I modified the serializer by overriding the to_representation function:
Alternatively to_representation can also be overriden like so to get the same effect for my specific dataset:
I'm leaving the question up for a few days in case anybody has another solution in mind, after that I'll mark this as the correct answer.
https://www.django-rest-framework.org/api-guide/fields/#serializermethodfield