Skip to content

Commit eb0b5ca

Browse files
author
Rocky Meza
committed
Add support for namespaces in APIRoot.
1 parent e6041f9 commit eb0b5ca

File tree

3 files changed

+62
-1
lines changed

3 files changed

+62
-1
lines changed

rest_framework/routers.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from collections import namedtuple
2020
from django.conf.urls import patterns, url
2121
from django.core.exceptions import ImproperlyConfigured
22-
from django.core.urlresolvers import NoReverseMatch
22+
from django.core.urlresolvers import NoReverseMatch, resolve, Resolver404
2323
from rest_framework import views
2424
from rest_framework.compat import OrderedDict
2525
from rest_framework.response import Response
@@ -290,9 +290,27 @@ def get_api_root_view(self):
290290
class APIRoot(views.APIView):
291291
_ignore_model_permissions = True
292292

293+
def get_namespace(self):
294+
"""
295+
Attempt to retrieve the namespace of the current router.
296+
"""
297+
try:
298+
# It appears that resolver_match is available on the
299+
# request object itself, but it isn't available during
300+
# tests.
301+
resolver_match = resolve(self.request.path_info)
302+
except Resolver404:
303+
# Because we are resolving the URL for the current request,
304+
# this should never fail, but it does during tests.
305+
return None
306+
else:
307+
return resolver_match.namespace
308+
293309
def get(self, request, *args, **kwargs):
294310
ret = OrderedDict()
311+
namespace = self.get_namespace()
295312
for key, url_name in api_root_dict.items():
313+
url_name = ':'.join(filter(bool, (namespace, url_name)))
296314
try:
297315
ret[key] = reverse(
298316
url_name,

tests/namespaced_urls.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from django.conf.urls import url, include
2+
from django.db import models
3+
4+
from rest_framework import serializers, viewsets, routers
5+
6+
7+
class NamespacedRouterTestModel(models.Model):
8+
uuid = models.CharField(max_length=20)
9+
text = models.CharField(max_length=200)
10+
11+
12+
class NoteSerializer(serializers.HyperlinkedModelSerializer):
13+
url = serializers.HyperlinkedIdentityField(view_name='api-namespace:routertestmodel-detail', lookup_field='uuid')
14+
15+
class Meta:
16+
model = NamespacedRouterTestModel
17+
fields = ('url', 'uuid', 'text')
18+
19+
20+
class NoteViewSet(viewsets.ModelViewSet):
21+
queryset = NamespacedRouterTestModel.objects.all()
22+
serializer_class = NoteSerializer
23+
lookup_field = 'uuid'
24+
25+
router = routers.DefaultRouter()
26+
27+
router.register(r'note', NoteViewSet)
28+
29+
30+
urlpatterns = [
31+
url('^namespaced-api/', include(router.urls, namespace='api-namespace')),
32+
]

tests/test_routers.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from django.db import models
44
from django.test import TestCase
55
from django.core.exceptions import ImproperlyConfigured
6+
from django.core import urlresolvers
67
from rest_framework import serializers, viewsets, mixins, permissions
78
from rest_framework.decorators import detail_route, list_route
89
from rest_framework.response import Response
@@ -321,3 +322,13 @@ def test_api_root(self):
321322
request = factory.get('/')
322323
response = self.view(request)
323324
self.assertEqual(response.data, {})
325+
326+
327+
class TestRootWithNamespacedURLs(TestCase):
328+
urls = 'tests.namespaced_urls'
329+
330+
def test_api_root_contains_routes(self):
331+
url = urlresolvers.reverse('api-namespace:api-root')
332+
response = self.client.get(url)
333+
self.assertIn('note', response.data)
334+
self.assertEqual(response.data['note'], 'http://testserver/namespaced-api/note/')

0 commit comments

Comments
 (0)