Skip to content
Open
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
9 changes: 5 additions & 4 deletions rest_framework/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
13 changes: 13 additions & 0 deletions tests/test_viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'}
Comment on lines +201 to +204
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So head is now never present in the viewset action... Another way to solve this mutation issue would be to make sure it's always there...

I'm not sure which way is actually more desirable

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both approaches work since self.action_map contains head either way. The only difference is whether view.actions reflects the original input or the effective mappings.

What's your thought on this?


def test_viewset_action_attr_for_extra_action(self):
view = ActionViewSet.as_view(actions=dict(ActionViewSet.list_action.mapping))

Expand Down
Loading