Support namespaced router URLs with DefaultRouter.

This commit is contained in:
Tom Christie 2014-12-28 12:02:52 +00:00
parent 67fc002f91
commit efa5942ce1
3 changed files with 76 additions and 33 deletions

View File

@ -50,6 +50,16 @@ except ImportError:
from django.http import HttpResponse as HttpResponseBase 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 # django-filter is optional
try: try:
import django_filters import django_filters

View File

@ -21,7 +21,7 @@ from django.conf.urls import patterns, url
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import NoReverseMatch from django.core.urlresolvers import NoReverseMatch
from rest_framework import views 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.response import Response
from rest_framework.reverse import reverse from rest_framework.reverse import reverse
from rest_framework.urlpatterns import format_suffix_patterns from rest_framework.urlpatterns import format_suffix_patterns
@ -292,7 +292,10 @@ class DefaultRouter(SimpleRouter):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
ret = OrderedDict() ret = OrderedDict()
namespace = get_resolver_match(request).namespace
for key, url_name in api_root_dict.items(): for key, url_name in api_root_dict.items():
if namespace:
url_name = namespace + ':' + url_name
try: try:
ret[key] = reverse( ret[key] = reverse(
url_name, url_name,

View File

@ -1,5 +1,5 @@
from __future__ import unicode_literals 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.db import models
from django.test import TestCase from django.test import TestCase
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
@ -12,7 +12,42 @@ from collections import namedtuple
factory = APIRequestFactory() 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): class BasicViewSet(viewsets.ViewSet):
@ -64,9 +99,26 @@ class TestSimpleRouter(TestCase):
self.assertEqual(route.mapping[method], endpoint) self.assertEqual(route.mapping[method], endpoint)
class RouterTestModel(models.Model): class TestRootView(TestCase):
uuid = models.CharField(max_length=20) urls = 'tests.test_routers'
text = models.CharField(max_length=200)
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): class TestCustomLookupFields(TestCase):
@ -76,51 +128,29 @@ class TestCustomLookupFields(TestCase):
urls = 'tests.test_routers' urls = 'tests.test_routers'
def setUp(self): 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') RouterTestModel.objects.create(uuid='123', text='foo bar')
def test_custom_lookup_field_route(self): 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 detail_url_pattern = detail_route.regex.pattern
self.assertIn('<uuid>', detail_url_pattern) self.assertIn('<uuid>', detail_url_pattern)
def test_retrieve_lookup_field_list_view(self): def test_retrieve_lookup_field_list_view(self):
response = self.client.get('/notes/') response = self.client.get('/example/notes/')
self.assertEqual( self.assertEqual(
response.data, response.data,
[{ [{
"url": "http://testserver/notes/123/", "url": "http://testserver/example/notes/123/",
"uuid": "123", "text": "foo bar" "uuid": "123", "text": "foo bar"
}] }]
) )
def test_retrieve_lookup_field_detail_view(self): def test_retrieve_lookup_field_detail_view(self):
response = self.client.get('/notes/123/') response = self.client.get('/example/notes/123/')
self.assertEqual( self.assertEqual(
response.data, response.data,
{ {
"url": "http://testserver/notes/123/", "url": "http://testserver/example/notes/123/",
"uuid": "123", "text": "foo bar" "uuid": "123", "text": "foo bar"
} }
) )