diff options
| -rw-r--r-- | docs/api-guide/routers.md | 6 | ||||
| -rw-r--r-- | rest_framework/routers.py | 17 | ||||
| -rw-r--r-- | rest_framework/tests/test_routers.py | 23 | 
3 files changed, 39 insertions, 7 deletions
diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md index 846ac9f9..249e99a4 100644 --- a/docs/api-guide/routers.md +++ b/docs/api-guide/routers.md @@ -83,6 +83,12 @@ This behavior can be modified by setting the `trailing_slash` argument to `False  Trailing slashes are conventional in Django, but are not used by default in some other frameworks such as Rails.  Which style you choose to use is largely a matter of preference, although some javascript frameworks may expect a particular routing style. +The router will match lookup values containing any characters except slashes and period characters.  For a more restrictive (or lenient) lookup pattern, set the `lookup_value_regex` attribute on the viewset.  For example, you can limit the lookup to valid UUIDs: + +    class MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet): +        lookup_field = 'my_model_id' +        lookup_value_regex = '[0-9a-f]{32}' +  ## DefaultRouter  This router is similar to `SimpleRouter` as above, but additionally includes a default API root view, that returns a response containing hyperlinks to all the list views.  It also generates routes for optional `.json` style format suffixes. diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 740d58f0..df1233fd 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -219,13 +219,18 @@ class SimpleRouter(BaseRouter):          https://github.com/alanjds/drf-nested-routers          """ -        if self.trailing_slash: -            base_regex = '(?P<{lookup_prefix}{lookup_field}>[^/]+)' -        else: -            # Don't consume `.json` style suffixes -            base_regex = '(?P<{lookup_prefix}{lookup_field}>[^/.]+)' +        base_regex = '(?P<{lookup_prefix}{lookup_field}>{lookup_value})'          lookup_field = getattr(viewset, 'lookup_field', 'pk') -        return base_regex.format(lookup_field=lookup_field, lookup_prefix=lookup_prefix) +        try: +            lookup_value = viewset.lookup_value_regex +        except AttributeError: +            # Don't consume `.json` style suffixes +            lookup_value = '[^/.]+' +        return base_regex.format( +            lookup_prefix=lookup_prefix, +            lookup_field=lookup_field, +            lookup_value=lookup_value +        )      def get_urls(self):          """ diff --git a/rest_framework/tests/test_routers.py b/rest_framework/tests/test_routers.py index 1c34648f..e41da57f 100644 --- a/rest_framework/tests/test_routers.py +++ b/rest_framework/tests/test_routers.py @@ -121,6 +121,27 @@ class TestCustomLookupFields(TestCase):          ) +class TestLookupValueRegex(TestCase): +    """ +    Ensure the router honors lookup_value_regex when applied +    to the viewset. +    """ +    def setUp(self): +        class NoteViewSet(viewsets.ModelViewSet): +            queryset = RouterTestModel.objects.all() +            lookup_field = 'uuid' +            lookup_value_regex = '[0-9a-f]{32}' + +        self.router = SimpleRouter() +        self.router.register(r'notes', NoteViewSet) +        self.urls = self.router.urls + +    def test_urls_limited_by_lookup_value_regex(self): +        expected = ['^notes/$', '^notes/(?P<uuid>[0-9a-f]{32})/$'] +        for idx in range(len(expected)): +            self.assertEqual(expected[idx], self.urls[idx].regex.pattern) + +  class TestTrailingSlashIncluded(TestCase):      def setUp(self):          class NoteViewSet(viewsets.ModelViewSet): @@ -131,7 +152,7 @@ class TestTrailingSlashIncluded(TestCase):          self.urls = self.router.urls      def test_urls_have_trailing_slash_by_default(self): -        expected = ['^notes/$', '^notes/(?P<pk>[^/]+)/$'] +        expected = ['^notes/$', '^notes/(?P<pk>[^/.]+)/$']          for idx in range(len(expected)):              self.assertEqual(expected[idx], self.urls[idx].regex.pattern)  | 
