Skip to content

Commit efa5942

Browse files
Support namespaced router URLs with DefaultRouter.
1 parent 67fc002 commit efa5942

File tree

3 files changed

+76
-33
lines changed

3 files changed

+76
-33
lines changed

rest_framework/compat.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ def unicode_to_repr(value):
5050
from django.http import HttpResponse as HttpResponseBase
5151

5252

53+
# request only provides `resolver_match` from 1.5 onwards.
54+
def get_resolver_match(request):
55+
try:
56+
return request.resolver_match
57+
except AttributeError:
58+
# Django < 1.5
59+
from django.core.urlresolvers import resolve
60+
return resolve(request.path_info)
61+
62+
5363
# django-filter is optional
5464
try:
5565
import django_filters

rest_framework/routers.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from django.core.exceptions import ImproperlyConfigured
2222
from django.core.urlresolvers import NoReverseMatch
2323
from rest_framework import views
24-
from rest_framework.compat import OrderedDict
24+
from rest_framework.compat import get_resolver_match, OrderedDict
2525
from rest_framework.response import Response
2626
from rest_framework.reverse import reverse
2727
from rest_framework.urlpatterns import format_suffix_patterns
@@ -292,7 +292,10 @@ class APIRoot(views.APIView):
292292

293293
def get(self, request, *args, **kwargs):
294294
ret = OrderedDict()
295+
namespace = get_resolver_match(request).namespace
295296
for key, url_name in api_root_dict.items():
297+
if namespace:
298+
url_name = namespace + ':' + url_name
296299
try:
297300
ret[key] = reverse(
298301
url_name,

tests/test_routers.py

Lines changed: 62 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from __future__ import unicode_literals
2-
from django.conf.urls import patterns, url, include
2+
from django.conf.urls import url, include
33
from django.db import models
44
from django.test import TestCase
55
from django.core.exceptions import ImproperlyConfigured
@@ -12,7 +12,42 @@
1212

1313
factory = APIRequestFactory()
1414

15-
urlpatterns = patterns('',)
15+
16+
class RouterTestModel(models.Model):
17+
uuid = models.CharField(max_length=20)
18+
text = models.CharField(max_length=200)
19+
20+
21+
class NoteSerializer(serializers.HyperlinkedModelSerializer):
22+
url = serializers.HyperlinkedIdentityField(view_name='routertestmodel-detail', lookup_field='uuid')
23+
24+
class Meta:
25+
model = RouterTestModel
26+
fields = ('url', 'uuid', 'text')
27+
28+
29+
class NoteViewSet(viewsets.ModelViewSet):
30+
queryset = RouterTestModel.objects.all()
31+
serializer_class = NoteSerializer
32+
lookup_field = 'uuid'
33+
34+
35+
class MockViewSet(viewsets.ModelViewSet):
36+
queryset = None
37+
serializer_class = None
38+
39+
40+
notes_router = SimpleRouter()
41+
notes_router.register(r'notes', NoteViewSet)
42+
43+
namespaced_router = DefaultRouter()
44+
namespaced_router.register(r'example', MockViewSet, base_name='example')
45+
46+
urlpatterns = [
47+
url(r'^non-namespaced/', include(namespaced_router.urls)),
48+
url(r'^namespaced/', include(namespaced_router.urls, namespace='example')),
49+
url(r'^example/', include(notes_router.urls)),
50+
]
1651

1752

1853
class BasicViewSet(viewsets.ViewSet):
@@ -64,9 +99,26 @@ def test_link_and_action_decorator(self):
6499
self.assertEqual(route.mapping[method], endpoint)
65100

66101

67-
class RouterTestModel(models.Model):
68-
uuid = models.CharField(max_length=20)
69-
text = models.CharField(max_length=200)
102+
class TestRootView(TestCase):
103+
urls = 'tests.test_routers'
104+
105+
def test_retrieve_namespaced_root(self):
106+
response = self.client.get('/namespaced/')
107+
self.assertEqual(
108+
response.data,
109+
{
110+
"example": "http://testserver/namespaced/example/",
111+
}
112+
)
113+
114+
def test_retrieve_non_namespaced_root(self):
115+
response = self.client.get('/non-namespaced/')
116+
self.assertEqual(
117+
response.data,
118+
{
119+
"example": "http://testserver/non-namespaced/example/",
120+
}
121+
)
70122

71123

72124
class TestCustomLookupFields(TestCase):
@@ -76,51 +128,29 @@ class TestCustomLookupFields(TestCase):
76128
urls = 'tests.test_routers'
77129

78130
def setUp(self):
79-
class NoteSerializer(serializers.HyperlinkedModelSerializer):
80-
url = serializers.HyperlinkedIdentityField(view_name='routertestmodel-detail', lookup_field='uuid')
81-
82-
class Meta:
83-
model = RouterTestModel
84-
fields = ('url', 'uuid', 'text')
85-
86-
class NoteViewSet(viewsets.ModelViewSet):
87-
queryset = RouterTestModel.objects.all()
88-
serializer_class = NoteSerializer
89-
lookup_field = 'uuid'
90-
91-
self.router = SimpleRouter()
92-
self.router.register(r'notes', NoteViewSet)
93-
94-
from tests import test_routers
95-
urls = getattr(test_routers, 'urlpatterns')
96-
urls += patterns(
97-
'',
98-
url(r'^', include(self.router.urls)),
99-
)
100-
101131
RouterTestModel.objects.create(uuid='123', text='foo bar')
102132

103133
def test_custom_lookup_field_route(self):
104-
detail_route = self.router.urls[-1]
134+
detail_route = notes_router.urls[-1]
105135
detail_url_pattern = detail_route.regex.pattern
106136
self.assertIn('<uuid>', detail_url_pattern)
107137

108138
def test_retrieve_lookup_field_list_view(self):
109-
response = self.client.get('/notes/')
139+
response = self.client.get('/example/notes/')
110140
self.assertEqual(
111141
response.data,
112142
[{
113-
"url": "http://testserver/notes/123/",
143+
"url": "http://testserver/example/notes/123/",
114144
"uuid": "123", "text": "foo bar"
115145
}]
116146
)
117147

118148
def test_retrieve_lookup_field_detail_view(self):
119-
response = self.client.get('/notes/123/')
149+
response = self.client.get('/example/notes/123/')
120150
self.assertEqual(
121151
response.data,
122152
{
123-
"url": "http://testserver/notes/123/",
153+
"url": "http://testserver/example/notes/123/",
124154
"uuid": "123", "text": "foo bar"
125155
}
126156
)

0 commit comments

Comments
 (0)