Skip to content

Commit fec50fb

Browse files
author
Ryan P Kilby
committed
Refactor dynamic routes
1 parent 787c14d commit fec50fb

File tree

1 file changed

+35
-45
lines changed

1 file changed

+35
-45
lines changed

rest_framework/routers.py

Lines changed: 35 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -160,57 +160,47 @@ def get_routes(self, viewset):
160160
# converting to list as iterables are good for one pass, known host needs to be checked again and again for
161161
# different functions.
162162
known_actions = list(flatten([route.mapping.values() for route in self.routes if isinstance(route, Route)]))
163-
164-
# Determine any `@detail_route` or `@list_route` decorated methods on the viewset
165-
detail_routes = []
166-
list_routes = []
167-
for methodname in dir(viewset):
168-
attr = getattr(viewset, methodname)
169-
httpmethods = getattr(attr, 'bind_to_methods', None)
170-
detail = getattr(attr, 'detail', True)
171-
if httpmethods:
172-
# checking method names against the known actions list
173-
if methodname in known_actions:
174-
raise ImproperlyConfigured('Cannot use @detail_route or @list_route '
175-
'decorators on method "%s" '
176-
'as it is an existing route' % methodname)
177-
httpmethods = [method.lower() for method in httpmethods]
178-
if detail:
179-
detail_routes.append((httpmethods, methodname))
180-
else:
181-
list_routes.append((httpmethods, methodname))
182-
183-
def _get_dynamic_routes(route, dynamic_routes):
184-
ret = []
185-
for httpmethods, methodname in dynamic_routes:
186-
method_kwargs = getattr(viewset, methodname).kwargs
187-
initkwargs = route.initkwargs.copy()
188-
initkwargs.update(method_kwargs)
189-
url_path = initkwargs.pop("url_path", None) or methodname
190-
url_path = escape_curly_brackets(url_path)
191-
url_name = initkwargs.pop("url_name", None) or url_path
192-
ret.append(Route(
193-
url=replace_methodname(route.url, url_path),
194-
mapping={httpmethod: methodname for httpmethod in httpmethods},
195-
name=replace_methodname(route.name, url_name),
196-
initkwargs=initkwargs,
197-
))
198-
199-
return ret
200-
201-
ret = []
163+
extra_actions = viewset.get_extra_actions()
164+
165+
# checking action names against the known actions list
166+
not_allowed = [
167+
action.__name__ for action in extra_actions
168+
if action.__name__ in known_actions
169+
]
170+
if not_allowed:
171+
msg = ('Cannot use the @action decorator on the following '
172+
'methods, as they are existing routes: %s')
173+
raise ImproperlyConfigured(msg % ', '.join(not_allowed))
174+
175+
# partition detail and list routes
176+
detail_actions = [action for action in extra_actions if action.detail]
177+
list_actions = [action for action in extra_actions if not action.detail]
178+
179+
routes = []
202180
for route in self.routes:
203181
if isinstance(route, DynamicDetailRoute):
204-
# Dynamic detail routes (@detail_route decorator)
205-
ret += _get_dynamic_routes(route, detail_routes)
182+
routes += [self._get_dynamic_route(route, action) for action in detail_actions]
206183
elif isinstance(route, DynamicListRoute):
207-
# Dynamic list routes (@list_route decorator)
208-
ret += _get_dynamic_routes(route, list_routes)
184+
routes += [self._get_dynamic_route(route, action) for action in list_actions]
209185
else:
210186
# Standard route
211-
ret.append(route)
187+
routes.append(route)
212188

213-
return ret
189+
return routes
190+
191+
def _get_dynamic_route(self, route, action):
192+
initkwargs = route.initkwargs.copy()
193+
initkwargs.update(action.kwargs)
194+
195+
url_path = escape_curly_brackets(action.url_path)
196+
197+
return Route(
198+
url=replace_methodname(route.url, url_path),
199+
name=replace_methodname(route.name, action.url_name),
200+
mapping={http_method: action.__name__
201+
for http_method in action.bind_to_methods},
202+
initkwargs=initkwargs,
203+
)
214204

215205
def get_method_map(self, viewset, method_map):
216206
"""

0 commit comments

Comments
 (0)