I am attempting to implement a nested resource where one of its fields depends on a value from its parent resource.
Suppose we are building a system for a company which
In addition to the accepted answer, if you use viewsets and want your sub-resource to be a collection (filtered by the parent document) only, you can also use the @detail_route
decorator, as documented in the docs:
from rest_framework import viewsets
from rest_framework.decorators import detail_route
from rest_framework.response import Response
class CustomerViewSet(viewsets.ModelViewSet):
queryset = Customer.objects.all()
serializer_class = CustomerSummarySerializer
...
@detail_route(methods=['get'])
def rep(self, request, pk=None):
customer = self.get_object()
queryset = customer.pk.all()
instances = self.filter_queryset(queryset)
serializer = RepSummarySerializer(instances,
context={'request': request}, many=True)
return Response(serializer.data)
Now you can query /customers/123/rep/
and you will get a list of all Rep
instances for the specified customer.
It probably won't fully solve what you need, but for many people that don't need full nested resources it's actually enough.
Moment of clarity: the solution is to use a SerializerMethodField
to instantiate the RepSummarySerializer
and pass the customer_id
in the context:
class CustomerSummarySerializer(serializers.HyperlinkedModelSerializer):
id = ...
name = ...
rep = serializers.SerializerMethodField('get_rep')
def get_rep(self, obj):
rep = obj.rep
serializer_context = {'request': self.context.get('request'),
'customer_id': obj.id}
serializer = RepSummarySerializer(rep, context=serializer_context)
return serializer.data
The customer_id
can now be accessed in RepSummarySerializer.get_rep_url
like this:
def get_rep_url(self, obj):
customer_id = self.context.get('customer_id')
...
Don't know why I didn't think of this three hours ago.