Skip to content

Instantly share code, notes, and snippets.

@kevin-brown
Last active November 28, 2019 09:28
Show Gist options
  • Save kevin-brown/6112838 to your computer and use it in GitHub Desktop.
Save kevin-brown/6112838 to your computer and use it in GitHub Desktop.

Nested ViewSets for DRF

DRF doesn't allow nested ViewSets natively, so I figured out the least amount of tweaks required to get it working.

The fields that are provided allow you to override the reverse() call with a specific mapping. These cannot be used across urls yet, as it pulls straight from the kwargs.

In your field, you must define map, which is a dictionary of destinaton => previous kwargs keys. These keys are relative to the current URL.

from rest_framework.serializers import HyperlinkedIdentityField, \
HyperlinkedRelatedField
class NestedHyperlinkedField(object):
base_obj = None
def __init__(self, *args, **kwargs):
self.map = kwargs.pop("map", {})
super(NestedHyperlinkedField, self).__init__(*args, **kwargs)
def field_to_native(self, obj, field_name):
import logging
self.base_obj = obj
return super(NestedHyperlinkedField, self).field_to_native(obj,
field_name)
def get_url(self, obj, view_name, request, format):
from django.core.urlresolvers import NoReverseMatch
from rest_framework.reverse import reverse
import logging
try:
return super(NestedHyperlinkedField, self).get_url(obj,
view_name, request, format)
except NoReverseMatch:
pass
kwargs = {
"pk": obj.pk,
}
serializer = self.parent
view = serializer.context["view"]
for key, kwkey in self.map.iteritems():
base_obj = self.base_obj.pk if self.base_obj else 0
kwargs[key] = view.kwargs.get(kwkey, base_obj)
url = reverse(view_name, kwargs=kwargs, request=request, format=format)
return url
class HyperlinkedIdentityField(NestedHyperlinkedField,
HyperlinkedIdentityField):
pass
class HyperlinkedRelatedField(NestedHyperlinkedField, HyperlinkedRelatedField):
pass
class ApiRouter(routers.DefaultRouter):
def get_api_root_view(self):
"""
Return a view to use as the API root.
"""
from django.core.urlresolvers import NoReverseMatch
from rest_framework import views
api_root_dict = {}
list_name = self.routes[0].name
for prefix, viewset, basename in self.registry:
api_root_dict[prefix] = list_name.format(basename=basename)
class APIRoot(views.APIView):
_ignore_model_permissions = True
def get(self, request, format=None):
ret = {}
for key, url_name in api_root_dict.items():
try:
ret[key] = reverse(url_name, request=request, format=format)
except NoReverseMatch:
pass
return Response(ret)
return APIRoot.as_view()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment