From eb0b5ca6dc75c9f51b43de94bebf4b13d6897a5e Mon Sep 17 00:00:00 2001 From: Rocky Meza Date: Wed, 24 Dec 2014 19:37:13 -0700 Subject: [PATCH] Add support for namespaces in APIRoot. --- rest_framework/routers.py | 20 +++++++++++++++++++- tests/namespaced_urls.py | 32 ++++++++++++++++++++++++++++++++ tests/test_routers.py | 11 +++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 tests/namespaced_urls.py diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 1cb65b1c0ec..2ebdb30bf78 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -19,7 +19,7 @@ from collections import namedtuple from django.conf.urls import patterns, url from django.core.exceptions import ImproperlyConfigured -from django.core.urlresolvers import NoReverseMatch +from django.core.urlresolvers import NoReverseMatch, resolve, Resolver404 from rest_framework import views from rest_framework.compat import OrderedDict from rest_framework.response import Response @@ -290,9 +290,27 @@ def get_api_root_view(self): class APIRoot(views.APIView): _ignore_model_permissions = True + def get_namespace(self): + """ + Attempt to retrieve the namespace of the current router. + """ + try: + # It appears that resolver_match is available on the + # request object itself, but it isn't available during + # tests. + resolver_match = resolve(self.request.path_info) + except Resolver404: + # Because we are resolving the URL for the current request, + # this should never fail, but it does during tests. + return None + else: + return resolver_match.namespace + def get(self, request, *args, **kwargs): ret = OrderedDict() + namespace = self.get_namespace() for key, url_name in api_root_dict.items(): + url_name = ':'.join(filter(bool, (namespace, url_name))) try: ret[key] = reverse( url_name, diff --git a/tests/namespaced_urls.py b/tests/namespaced_urls.py new file mode 100644 index 00000000000..5fea59f9c88 --- /dev/null +++ b/tests/namespaced_urls.py @@ -0,0 +1,32 @@ +from django.conf.urls import url, include +from django.db import models + +from rest_framework import serializers, viewsets, routers + + +class NamespacedRouterTestModel(models.Model): + uuid = models.CharField(max_length=20) + text = models.CharField(max_length=200) + + +class NoteSerializer(serializers.HyperlinkedModelSerializer): + url = serializers.HyperlinkedIdentityField(view_name='api-namespace:routertestmodel-detail', lookup_field='uuid') + + class Meta: + model = NamespacedRouterTestModel + fields = ('url', 'uuid', 'text') + + +class NoteViewSet(viewsets.ModelViewSet): + queryset = NamespacedRouterTestModel.objects.all() + serializer_class = NoteSerializer + lookup_field = 'uuid' + +router = routers.DefaultRouter() + +router.register(r'note', NoteViewSet) + + +urlpatterns = [ + url('^namespaced-api/', include(router.urls, namespace='api-namespace')), +] diff --git a/tests/test_routers.py b/tests/test_routers.py index 06ab8103ada..fb63ad9d718 100644 --- a/tests/test_routers.py +++ b/tests/test_routers.py @@ -3,6 +3,7 @@ from django.db import models from django.test import TestCase from django.core.exceptions import ImproperlyConfigured +from django.core import urlresolvers from rest_framework import serializers, viewsets, mixins, permissions from rest_framework.decorators import detail_route, list_route from rest_framework.response import Response @@ -321,3 +322,13 @@ def test_api_root(self): request = factory.get('/') response = self.view(request) self.assertEqual(response.data, {}) + + +class TestRootWithNamespacedURLs(TestCase): + urls = 'tests.namespaced_urls' + + def test_api_root_contains_routes(self): + url = urlresolvers.reverse('api-namespace:api-root') + response = self.client.get(url) + self.assertIn('note', response.data) + self.assertEqual(response.data['note'], 'http://testserver/namespaced-api/note/')