diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 772ef3c2c..9a6f3c3c5 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -604,10 +604,13 @@ class BrowsableAPIRenderer(BaseRenderer): def get_breadcrumbs(self, request): return get_breadcrumbs(request.path, request) - def get_extra_actions(self, view): - if hasattr(view, 'get_extra_action_url_map'): - return view.get_extra_action_url_map() - return None + def get_extra_actions(self, view, status_code): + if (status_code in (status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN)): + return None + elif not hasattr(view, 'get_extra_action_url_map'): + return None + + return view.get_extra_action_url_map() def get_filter_form(self, data, view, request): if not hasattr(view, 'get_queryset') or not hasattr(view, 'filter_backends'): @@ -695,7 +698,7 @@ class BrowsableAPIRenderer(BaseRenderer): 'delete_form': self.get_rendered_html_form(data, view, 'DELETE', request), 'options_form': self.get_rendered_html_form(data, view, 'OPTIONS', request), - 'extra_actions': self.get_extra_actions(view), + 'extra_actions': self.get_extra_actions(view, response.status_code), 'filter_form': self.get_filter_form(data, view, request), diff --git a/tests/test_renderers.py b/tests/test_renderers.py index d63dbcb9c..c79c0a766 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -631,8 +631,12 @@ class BrowsableAPIRendererTests(URLPatternsTestCase): def list_action(self, request): raise NotImplementedError + class AuthExampleViewSet(ExampleViewSet): + permission_classes = [permissions.IsAuthenticated] + router = SimpleRouter() router.register('examples', ExampleViewSet, basename='example') + router.register('auth-examples', AuthExampleViewSet, basename='auth-example') urlpatterns = [url(r'^api/', include(router.urls))] def setUp(self): @@ -657,6 +661,12 @@ class BrowsableAPIRendererTests(URLPatternsTestCase): assert '/api/examples/list_action/' in resp.content.decode() assert '>Extra list action<' in resp.content.decode() + def test_extra_actions_dropdown_not_authed(self): + resp = self.client.get('/api/unauth-examples/', HTTP_ACCEPT='text/html') + assert 'id="extra-actions-menu"' not in resp.content.decode() + assert '/api/examples/list_action/' not in resp.content.decode() + assert '>Extra list action<' not in resp.content.decode() + class AdminRendererTests(TestCase):