From 4ba48d34602237f3862dc2377c4422b2d82aa15e Mon Sep 17 00:00:00 2001 From: Rocky Meza Date: Sun, 21 Dec 2014 03:46:54 -0700 Subject: [PATCH] Add support for namespaces to DefaultRouter. --- docs/api-guide/routers.md | 11 +++++++++++ rest_framework/routers.py | 8 ++++++++ tests/namespaced_urls.py | 32 ++++++++++++++++++++++++++++++++ tests/test_routers.py | 10 ++++++++++ 4 files changed, 61 insertions(+) create mode 100644 tests/namespaced_urls.py diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md index 6819adb6a..7cf07551d 100644 --- a/docs/api-guide/routers.md +++ b/docs/api-guide/routers.md @@ -140,6 +140,17 @@ As with `SimpleRouter` the trailing slashes on the URL routes can be removed by router = DefaultRouter(trailing_slash=False) +If you wish to put the router namespace, you will need to pass the `namespace` name to `DefaultRouter`. + + router = DefaultRouter(namespace='api') + urlpatterns = [ + url(r'^', include(router.urls, namespace='api')), + ] + +Please note if you do this, you will have to specify your namespace in your serializers too. + + url = serializers.HyperlinkedIdentityField(view_name='api:mymodel-detail') + # Custom Routers Implementing a custom router isn't something you'd need to do very often, but it can be useful if you have specific requirements about how the your URLs for your API are structured. Doing so allows you to encapsulate the URL structure in a reusable way that ensures you don't have to write your URL patterns explicitly for each new view. diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 1cb65b1c0..b9a3c4277 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -278,6 +278,10 @@ class DefaultRouter(SimpleRouter): include_format_suffixes = True root_view_name = 'api-root' + def __init__(self, *args, **kwargs): + self.namespace = kwargs.pop('namespace', None) + super(DefaultRouter, self).__init__(*args, **kwargs) + def get_api_root_view(self): """ Return a view to use as the API root. @@ -287,12 +291,16 @@ class DefaultRouter(SimpleRouter): for prefix, viewset, basename in self.registry: api_root_dict[prefix] = list_name.format(basename=basename) + namespace = self.namespace + class APIRoot(views.APIView): _ignore_model_permissions = True def get(self, request, *args, **kwargs): ret = OrderedDict() for key, url_name in api_root_dict.items(): + if namespace: + url_name = '%s:%s' % (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 000000000..1c2252a92 --- /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(namespace='api-namespace') + +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 06ab8103a..04e5bcebd 100644 --- a/tests/test_routers.py +++ b/tests/test_routers.py @@ -321,3 +321,13 @@ class TestRootWithAListlessViewset(TestCase): request = factory.get('/') response = self.view(request) self.assertEqual(response.data, {}) + + +class TestNamespacedDefaultRouter(TestCase): + urls = 'tests.namespaced_urls' + + def test_api_root(self): + from django.core.urlresolvers import reverse + url = reverse('api-namespace:api-root') + response = self.client.get(url) + self.assertEqual(response.data['note'], 'http://testserver/namespaced-api/note/')