Reputation: 10055
I'm trying to serialize a MPTT tree model with DRF.
My code:
class SiteTreeCalc(serializers.Field):
def to_representation(self, value):
return value.exists() # return True if has children, False otherwise
class SiteTreeSerializer(serializers.ModelSerializer):
children = SiteTreeCalc()
class Meta:
model = SiteTree
fields = ('id', 'site', 'children')
depth = 1
class SiteTreeViewSet(viewsets.ModelViewSet):
#queryset = SiteTree.objects.all()
serializer_class = SiteTreeSerializer
def get_queryset(self):
if 'pk' not in self.kwargs:
# return first-level nodes
return SiteTree.objects.filter(level=0)
else:
# return all children of a given node
return SiteTree.objects.filter(parent__id=int(self.kwargs['pk']))
router = routers.DefaultRouter()
router.register(r'rest/sitetree', SiteTreeViewSet, "SiteTreeRoots")
router.register(r'rest/sitetree/(?P<tree_id>\d+)/$', SiteTreeViewSet, "SiteTreeChildren")
I have two issues with this code:
get_queryset
says that parameter name is pk
I seem to be doing something wrong, but the code seems so obvious to me that I just can't see it.
Help - as always - very appreciated.
Upvotes: 3
Views: 123
Reputation: 10055
Turns out, I wanted to forget the convenient functionality of DefaultRouter the first chance I got.
The problem was that I wanted to create a ViewSet just like any other writable ViewSet, but this particular one was intended only for retrieving items. At least, that's what I intended. But DRF couldn't know that, so my problem #2 was a result of DRF actually checking that I'm returning ONE item with EXACTLY the same pk as was given in the URL.
A solution that works goes like this (as suggested in the DRF ViewSets documentation):
class SiteTreeViewSet(viewsets.ReadOnlyModelViewSet):
queryset = SiteTree.objects.filter(level=0)
serializer_class = SiteTreeSerializer
@detail_route()
def children(self, request, pk=None):
data = SiteTree.objects.filter(parent__id=int(pk))
data = self.get_serializer(data, many=True)
return Response(data.data)
This solution returns first-level items in default mode and also accepts /{pk}/children
to return children of the given pk node. Naturally, default operations will still return just the pk node when provided with a /{pk}/
URL.
Router registration remains only the default one:
router.register(r'rest/sitetree', SiteTreeViewSet)
Upvotes: 1
Reputation: 20996
As for 1. you need to set lookup_url_kwarg (the named argument in the urls) on the viewset so it maps to the tree_id.
Note that routers do define the dynamic url part themselves.
As for 2. it's most of the time sending JOSN POST data with form content type. Ensure you are sending the right content type in your request's header.
Edit: Daniel has the point for 1. With your current url patterns, there's no way to distinguish a detailed top node and a list for the child node.
Upvotes: 0