mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-03 12:00:12 +03:00
Refactor dynamic route generation
This commit is contained in:
parent
c2476fc035
commit
034f8d0283
|
@ -130,7 +130,7 @@ def schema(view_inspector):
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def action(methods=None, detail=True, **kwargs):
|
def action(methods=None, detail=True, url_path=None, url_name=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Mark a ViewSet method as a routable action.
|
Mark a ViewSet method as a routable action.
|
||||||
|
|
||||||
|
@ -143,6 +143,8 @@ def action(methods=None, detail=True, **kwargs):
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
func.bind_to_methods = methods
|
func.bind_to_methods = methods
|
||||||
func.detail = detail
|
func.detail = detail
|
||||||
|
func.url_path = url_path or func.__name__
|
||||||
|
func.url_name = url_name or func.__name__.replace('_', '-')
|
||||||
func.kwargs = kwargs
|
func.kwargs = kwargs
|
||||||
return func
|
return func
|
||||||
return decorator
|
return decorator
|
||||||
|
|
|
@ -66,18 +66,6 @@ def escape_curly_brackets(url_path):
|
||||||
return url_path
|
return url_path
|
||||||
|
|
||||||
|
|
||||||
def replace_methodname(format_string, methodname):
|
|
||||||
"""
|
|
||||||
Partially format a format_string, swapping out any
|
|
||||||
'{methodname}' or '{methodnamehyphen}' components.
|
|
||||||
"""
|
|
||||||
methodnamehyphen = methodname.replace('_', '-')
|
|
||||||
ret = format_string
|
|
||||||
ret = ret.replace('{methodname}', methodname)
|
|
||||||
ret = ret.replace('{methodnamehyphen}', methodnamehyphen)
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
def flatten(list_of_lists):
|
def flatten(list_of_lists):
|
||||||
"""
|
"""
|
||||||
Takes an iterable of iterables, returns a single iterable containing all items
|
Takes an iterable of iterables, returns a single iterable containing all items
|
||||||
|
@ -131,8 +119,8 @@ class SimpleRouter(BaseRouter):
|
||||||
# Dynamically generated list routes. Generated using
|
# Dynamically generated list routes. Generated using
|
||||||
# @action(detail=False) decorator on methods of the viewset.
|
# @action(detail=False) decorator on methods of the viewset.
|
||||||
DynamicRoute(
|
DynamicRoute(
|
||||||
url=r'^{prefix}/{methodname}{trailing_slash}$',
|
url=r'^{prefix}/{url_path}{trailing_slash}$',
|
||||||
name='{basename}-{methodnamehyphen}',
|
name='{basename}-{url_name}',
|
||||||
detail=False,
|
detail=False,
|
||||||
initkwargs={}
|
initkwargs={}
|
||||||
),
|
),
|
||||||
|
@ -152,8 +140,8 @@ class SimpleRouter(BaseRouter):
|
||||||
# Dynamically generated detail routes. Generated using
|
# Dynamically generated detail routes. Generated using
|
||||||
# @action(detail=True) decorator on methods of the viewset.
|
# @action(detail=True) decorator on methods of the viewset.
|
||||||
DynamicRoute(
|
DynamicRoute(
|
||||||
url=r'^{prefix}/{lookup}/{methodname}{trailing_slash}$',
|
url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$',
|
||||||
name='{basename}-{methodnamehyphen}',
|
name='{basename}-{url_name}',
|
||||||
detail=True,
|
detail=True,
|
||||||
initkwargs={}
|
initkwargs={}
|
||||||
),
|
),
|
||||||
|
@ -201,36 +189,32 @@ class SimpleRouter(BaseRouter):
|
||||||
detail_actions = [action for action in extra_actions if action.detail]
|
detail_actions = [action for action in extra_actions if action.detail]
|
||||||
list_actions = [action for action in extra_actions if not action.detail]
|
list_actions = [action for action in extra_actions if not action.detail]
|
||||||
|
|
||||||
def _get_dynamic_routes(route, dynamic_routes):
|
|
||||||
ret = []
|
|
||||||
for httpmethods, methodname in dynamic_routes:
|
|
||||||
method_kwargs = getattr(viewset, methodname).kwargs
|
|
||||||
initkwargs = route.initkwargs.copy()
|
|
||||||
initkwargs.update(method_kwargs)
|
|
||||||
url_path = initkwargs.pop("url_path", None) or methodname
|
|
||||||
url_path = escape_curly_brackets(url_path)
|
|
||||||
url_name = initkwargs.pop("url_name", None) or url_path
|
|
||||||
ret.append(Route(
|
|
||||||
url=replace_methodname(route.url, url_path),
|
|
||||||
mapping={httpmethod: methodname for httpmethod in httpmethods},
|
|
||||||
name=replace_methodname(route.name, url_name),
|
|
||||||
detail=route.detail,
|
|
||||||
initkwargs=initkwargs,
|
|
||||||
))
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
routes = []
|
routes = []
|
||||||
for route in self.routes:
|
for route in self.routes:
|
||||||
if isinstance(route, DynamicRoute) and route.detail:
|
if isinstance(route, DynamicRoute) and route.detail:
|
||||||
routes += _get_dynamic_routes(route, detail_actions)
|
routes += [self._get_dynamic_route(route, action) for action in detail_actions]
|
||||||
elif isinstance(route, DynamicRoute) and not route.detail:
|
elif isinstance(route, DynamicRoute) and not route.detail:
|
||||||
routes += _get_dynamic_routes(route, list_actions)
|
routes += [self._get_dynamic_route(route, action) for action in list_actions]
|
||||||
else:
|
else:
|
||||||
routes.append(route)
|
routes.append(route)
|
||||||
|
|
||||||
return routes
|
return routes
|
||||||
|
|
||||||
|
def _get_dynamic_route(self, route, action):
|
||||||
|
initkwargs = route.initkwargs.copy()
|
||||||
|
initkwargs.update(action.kwargs)
|
||||||
|
|
||||||
|
url_path = escape_curly_brackets(action.url_path)
|
||||||
|
|
||||||
|
return Route(
|
||||||
|
url=route.url.replace('{url_path}', url_path),
|
||||||
|
mapping={http_method: action.__name__
|
||||||
|
for http_method in action.bind_to_methods},
|
||||||
|
name=route.name.replace('{url_name}', action.url_name),
|
||||||
|
detail=route.detail,
|
||||||
|
initkwargs=initkwargs,
|
||||||
|
)
|
||||||
|
|
||||||
def get_method_map(self, viewset, method_map):
|
def get_method_map(self, viewset, method_map):
|
||||||
"""
|
"""
|
||||||
Given a viewset, and a mapping of http methods to actions,
|
Given a viewset, and a mapping of http methods to actions,
|
||||||
|
|
|
@ -179,6 +179,8 @@ class ActionDecoratorTestCase(TestCase):
|
||||||
|
|
||||||
assert test_action.bind_to_methods == ['get']
|
assert test_action.bind_to_methods == ['get']
|
||||||
assert test_action.detail is True
|
assert test_action.detail is True
|
||||||
|
assert test_action.url_path == 'test_action'
|
||||||
|
assert test_action.url_name == 'test-action'
|
||||||
|
|
||||||
def test_detail_route_deprecation(self):
|
def test_detail_route_deprecation(self):
|
||||||
with pytest.warns(PendingDeprecationWarning) as record:
|
with pytest.warns(PendingDeprecationWarning) as record:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user