aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/api-guide/routers.md6
-rw-r--r--rest_framework/routers.py20
-rw-r--r--rest_framework/tests/test_routers.py21
3 files changed, 41 insertions, 6 deletions
diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md
index 846ac9f9..f3beabdd 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.
+With `trailing_slash` set to True, the router will match lookup values containing any characters except slashes and dots. When set to False, dots are allowed. To restrict the lookup pattern, set the `lookup_field_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..8766ecb2 100644
--- a/rest_framework/routers.py
+++ b/rest_framework/routers.py
@@ -219,13 +219,21 @@ 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:
+ if self.trailing_slash:
+ lookup_value = '[^/]+'
+ else:
+ # 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..0f6d62c7 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):