mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-11 04:07:39 +03:00
Allow viewset to specify lookup value regex for routing
This patch allows a viewset to define a pattern for its lookup field, which the router will honor. Without this patch, any characters are allowed in the lookup field, and overriding this behavior requires subclassing router and copying and pasting the implementation of get_lookup_regex. It's possible it would be better to remove this functionality from the routers and simply expose a parameter to get_lookup_regex which allows overriding the lookup_regex. That way the viewset config logic could be in the a subclass, which could invoke the super method directly. I'm using this now for PostgreSQL UUID fields using https://github.com/dcramer/django-uuidfield . Without this patch, that field passes the lookup string to the database driver, which raises a DataError to complain about the invalid UUID. It's possible the field ought to signal this error in a different way, which could obviate the need to specify a pattern.
This commit is contained in:
parent
52686420f4
commit
a1d7aa8f71
|
@ -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.
|
||||
|
|
|
@ -219,13 +219,21 @@ class SimpleRouter(BaseRouter):
|
|||
|
||||
https://github.com/alanjds/drf-nested-routers
|
||||
"""
|
||||
base_regex = '(?P<{lookup_prefix}{lookup_field}>{lookup_value})'
|
||||
lookup_field = getattr(viewset, 'lookup_field', 'pk')
|
||||
try:
|
||||
lookup_value = viewset.lookup_value_regex
|
||||
except AttributeError:
|
||||
if self.trailing_slash:
|
||||
base_regex = '(?P<{lookup_prefix}{lookup_field}>[^/]+)'
|
||||
lookup_value = '[^/]+'
|
||||
else:
|
||||
# Don't consume `.json` style suffixes
|
||||
base_regex = '(?P<{lookup_prefix}{lookup_field}>[^/.]+)'
|
||||
lookup_field = getattr(viewset, 'lookup_field', 'pk')
|
||||
return base_regex.format(lookup_field=lookup_field, lookup_prefix=lookup_prefix)
|
||||
lookup_value = '[^/.]+'
|
||||
return base_regex.format(
|
||||
lookup_prefix=lookup_prefix,
|
||||
lookup_field=lookup_field,
|
||||
lookup_value=lookup_value
|
||||
)
|
||||
|
||||
def get_urls(self):
|
||||
"""
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue
Block a user