Skip to content

Commit

Permalink
Support namespaced router URLs with DefaultRouter.
Browse files Browse the repository at this point in the history
  • Loading branch information
tomchristie committed Dec 28, 2014
1 parent 67fc002 commit efa5942
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 33 deletions.
10 changes: 10 additions & 0 deletions rest_framework/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ def unicode_to_repr(value):
from django.http import HttpResponse as HttpResponseBase


# request only provides `resolver_match` from 1.5 onwards.
def get_resolver_match(request):
try:
return request.resolver_match
except AttributeError:
# Django < 1.5
from django.core.urlresolvers import resolve
return resolve(request.path_info)


# django-filter is optional
try:
import django_filters
Expand Down
5 changes: 4 additions & 1 deletion rest_framework/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import NoReverseMatch
from rest_framework import views
from rest_framework.compat import OrderedDict
from rest_framework.compat import get_resolver_match, OrderedDict
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.urlpatterns import format_suffix_patterns
Expand Down Expand Up @@ -292,7 +292,10 @@ class APIRoot(views.APIView):

def get(self, request, *args, **kwargs):
ret = OrderedDict()
namespace = get_resolver_match(request).namespace
for key, url_name in api_root_dict.items():
if namespace:
url_name = namespace + ':' + url_name
try:
ret[key] = reverse(
url_name,
Expand Down
94 changes: 62 additions & 32 deletions tests/test_routers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
from django.conf.urls import patterns, url, include
from django.conf.urls import url, include
from django.db import models
from django.test import TestCase
from django.core.exceptions import ImproperlyConfigured
Expand All @@ -12,7 +12,42 @@

factory = APIRequestFactory()

urlpatterns = patterns('',)

class RouterTestModel(models.Model):
uuid = models.CharField(max_length=20)
text = models.CharField(max_length=200)


class NoteSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='routertestmodel-detail', lookup_field='uuid')

class Meta:
model = RouterTestModel
fields = ('url', 'uuid', 'text')


class NoteViewSet(viewsets.ModelViewSet):
queryset = RouterTestModel.objects.all()
serializer_class = NoteSerializer
lookup_field = 'uuid'


class MockViewSet(viewsets.ModelViewSet):
queryset = None
serializer_class = None


notes_router = SimpleRouter()
notes_router.register(r'notes', NoteViewSet)

namespaced_router = DefaultRouter()
namespaced_router.register(r'example', MockViewSet, base_name='example')

urlpatterns = [
url(r'^non-namespaced/', include(namespaced_router.urls)),
url(r'^namespaced/', include(namespaced_router.urls, namespace='example')),
url(r'^example/', include(notes_router.urls)),
]


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


class RouterTestModel(models.Model):
uuid = models.CharField(max_length=20)
text = models.CharField(max_length=200)
class TestRootView(TestCase):
urls = 'tests.test_routers'

def test_retrieve_namespaced_root(self):
response = self.client.get('/namespaced/')
self.assertEqual(
response.data,
{
"example": "http://testserver/namespaced/example/",
}
)

def test_retrieve_non_namespaced_root(self):
response = self.client.get('/non-namespaced/')
self.assertEqual(
response.data,
{
"example": "http://testserver/non-namespaced/example/",
}
)


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

def setUp(self):
class NoteSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='routertestmodel-detail', lookup_field='uuid')

class Meta:
model = RouterTestModel
fields = ('url', 'uuid', 'text')

class NoteViewSet(viewsets.ModelViewSet):
queryset = RouterTestModel.objects.all()
serializer_class = NoteSerializer
lookup_field = 'uuid'

self.router = SimpleRouter()
self.router.register(r'notes', NoteViewSet)

from tests import test_routers
urls = getattr(test_routers, 'urlpatterns')
urls += patterns(
'',
url(r'^', include(self.router.urls)),
)

RouterTestModel.objects.create(uuid='123', text='foo bar')

def test_custom_lookup_field_route(self):
detail_route = self.router.urls[-1]
detail_route = notes_router.urls[-1]
detail_url_pattern = detail_route.regex.pattern
self.assertIn('<uuid>', detail_url_pattern)

def test_retrieve_lookup_field_list_view(self):
response = self.client.get('/notes/')
response = self.client.get('/example/notes/')
self.assertEqual(
response.data,
[{
"url": "http://testserver/notes/123/",
"url": "http://testserver/example/notes/123/",
"uuid": "123", "text": "foo bar"
}]
)

def test_retrieve_lookup_field_detail_view(self):
response = self.client.get('/notes/123/')
response = self.client.get('/example/notes/123/')
self.assertEqual(
response.data,
{
"url": "http://testserver/notes/123/",
"url": "http://testserver/example/notes/123/",
"uuid": "123", "text": "foo bar"
}
)
Expand Down

0 comments on commit efa5942

Please sign in to comment.