From bf561559bdd0c0fd510d04deafddaf0e7486a490 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 4 Apr 2018 12:06:14 -0400 Subject: [PATCH] Use old url_name behavior in route decorators --- docs/topics/release-notes.md | 4 ++++ rest_framework/decorators.py | 14 ++++++++++---- tests/test_decorators.py | 10 ++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 820fa731b..846750c9f 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -52,6 +52,8 @@ You can determine your currently installed version using `pip show`: * Merged `list_route` and `detail_route` into a single `action` decorator. * Get all extra actions on a `ViewSet` with `.get_extra_actions()`. * Extra actions now set the `url_name` and `url_path` on the decorated method. + * `url_name` is now based on the function name, instead of the `url_path`, + as the path is not always suitable (e.g., capturing arguments in the path). * Enable action url reversing through `.reverse_action()` method (added in 3.7.4) * Example reverse call: `self.reverse_action(self.custom_action.url_name)` * Add `detail` initkwarg to indicate if the current action is operating on a @@ -62,6 +64,8 @@ You can determine your currently installed version using `pip show`: * Deprecated `list_route` & `detail_route` in favor of `action` decorator with `detail` boolean. * Deprecated dynamic list/detail route variants in favor of `DynamicRoute` with `detail` boolean. * Refactored the router's dynamic route generation. + * `list_route` and `detail_route` maintain the old behavior of `url_name`, + basing it on the `url_path` instead of the function name. ## 3.7.x series diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index 62afa0597..415105c76 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -147,8 +147,14 @@ def action(methods=None, detail=None, url_path=None, url_name=None, **kwargs): def decorator(func): func.bind_to_methods = methods func.detail = detail - func.url_path = url_path or func.__name__ - func.url_name = url_name or func.__name__.replace('_', '-') + func.url_path = url_path if url_path else func.__name__ + func.url_name = url_name + if not url_name: + # Remove in 3.10 + if kwargs.get('_url_name_from_path', False): + func.url_name = func.url_path.replace('_', '-') + else: + func.url_name = func.__name__.replace('_', '-') func.kwargs = kwargs return func return decorator @@ -163,7 +169,7 @@ def detail_route(methods=None, **kwargs): "`action`, which accepts a `detail` bool. Use `@action(detail=True)` instead.", PendingDeprecationWarning, stacklevel=2 ) - return action(methods, detail=True, **kwargs) + return action(methods, detail=True, _url_name_from_path=True, **kwargs) def list_route(methods=None, **kwargs): @@ -175,4 +181,4 @@ def list_route(methods=None, **kwargs): "`action`, which accepts a `detail` bool. Use `@action(detail=False)` instead.", PendingDeprecationWarning, stacklevel=2 ) - return action(methods, detail=False, **kwargs) + return action(methods, detail=False, _url_name_from_path=True, **kwargs) diff --git a/tests/test_decorators.py b/tests/test_decorators.py index 674990730..fc80b472d 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -215,3 +215,13 @@ class ActionDecoratorTestCase(TestCase): "3.10 in favor of `action`, which accepts a `detail` bool. Use " "`@action(detail=False)` instead." ) + + def test_route_url_name_from_path(self): + # pre-3.8 behavior was to base the `url_name` off of the `url_path` + with pytest.warns(PendingDeprecationWarning): + @list_route(url_path='foo_bar') + def view(request): + pass + + assert view.url_path == 'foo_bar' + assert view.url_name == 'foo-bar'