Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions rest_framework/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
"""
from __future__ import unicode_literals

import itertools
from collections import namedtuple
from django.core.exceptions import ImproperlyConfigured
from rest_framework import views
from rest_framework.compat import patterns, url
from rest_framework.response import Response
Expand All @@ -38,6 +40,13 @@ def replace_methodname(format_string, methodname):
return ret


def flatten(list_of_lists):
"""
Takes an iterable of iterables, returns a single iterable containing all items
"""
return itertools.chain(*list_of_lists)


class BaseRouter(object):
def __init__(self):
self.registry = []
Expand Down Expand Up @@ -130,12 +139,17 @@ def get_routes(self, viewset):
Returns a list of the Route namedtuple.
"""

known_actions = flatten([route.mapping.values() for route in self.routes])

# Determine any `@action` or `@link` decorated methods on the viewset
dynamic_routes = []
for methodname in dir(viewset):
attr = getattr(viewset, methodname)
httpmethods = getattr(attr, 'bind_to_methods', None)
if httpmethods:
if methodname in known_actions:
raise ImproperlyConfigured('Cannot use @action or @link decorator on '
'method "%s" as it is an existing route' % methodname)
httpmethods = [method.lower() for method in httpmethods]
dynamic_routes.append((httpmethods, methodname))

Expand Down
22 changes: 22 additions & 0 deletions rest_framework/tests/test_routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from django.db import models
from django.test import TestCase
from django.test.client import RequestFactory
from django.core.exceptions import ImproperlyConfigured
from rest_framework import serializers, viewsets, permissions
from rest_framework.compat import include, patterns, url
from rest_framework.decorators import link, action
Expand Down Expand Up @@ -191,3 +192,24 @@ def test_action_kwargs(self):
response.data,
{'permission_classes': [permissions.AllowAny]}
)

class TestActionAppliedToExistingRoute(TestCase):
"""
Ensure `@action` decorator raises an except when applied
to an existing route
"""

def test_exception_raised_when_action_applied_to_existing_route(self):
class TestViewSet(viewsets.ModelViewSet):

@action()
def retrieve(self, request, *args, **kwargs):
return Response({
'hello': 'world'
})

self.router = SimpleRouter()
self.router.register(r'test', TestViewSet, base_name='test')

with self.assertRaises(ImproperlyConfigured):
self.router.urls