diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py index a9c90a8d9f..7226b1c046 100644 --- a/rest_framework/viewsets.py +++ b/rest_framework/viewsets.py @@ -103,17 +103,18 @@ def as_view(cls, actions=None, **initkwargs): def view(request, *args, **kwargs): self = cls(**initkwargs) - if 'get' in actions and 'head' not in actions: - actions['head'] = actions['get'] + bound_actions = dict(actions) + if 'get' in bound_actions and 'head' not in bound_actions: + bound_actions['head'] = bound_actions['get'] # We also store the mapping of request methods to actions, # so that we can later set the action attribute. # eg. `self.action = 'list'` on an incoming GET request. - self.action_map = actions + self.action_map = bound_actions # Bind methods to actions # This is the bit that's different to a standard view - for method, action in actions.items(): + for method, action in bound_actions.items(): handler = getattr(self, action) setattr(self, method, handler) diff --git a/tests/test_viewsets.py b/tests/test_viewsets.py index 68b1207c39..3e1c73f5cc 100644 --- a/tests/test_viewsets.py +++ b/tests/test_viewsets.py @@ -190,6 +190,19 @@ def test_viewset_action_attr(self): assert get.view.action == 'list' assert head.view.action == 'list' + def test_actions_dict_not_mutated(self): + """ + The original actions dict passed to as_view() should not be mutated. + Regression test for #9747. + """ + actions = {'get': 'list'} + view = ActionViewSet.as_view(actions=actions) + + assert 'head' not in actions + view(factory.get('/')) + assert 'head' not in actions + assert view.actions == {'get': 'list'} + def test_viewset_action_attr_for_extra_action(self): view = ActionViewSet.as_view(actions=dict(ActionViewSet.list_action.mapping))