From 95714730f9996d37fe0cd11ca431d5fef5fc6b88 Mon Sep 17 00:00:00 2001 From: Peter Malmgren Date: Fri, 23 Oct 2015 15:44:35 -0400 Subject: [PATCH] Skipped the object permission check for a list_route decorated view with a PUT method --- rest_framework/metadata.py | 6 +++++- tests/test_metadata.py | 40 +++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/rest_framework/metadata.py b/rest_framework/metadata.py index 6c4f17692..3f4fe8af8 100644 --- a/rest_framework/metadata.py +++ b/rest_framework/metadata.py @@ -85,7 +85,11 @@ class SimpleMetadata(BaseMetadata): view.check_permissions(view.request) # Test object permissions if method == 'PUT' and hasattr(view, 'get_object'): - view.get_object() + # List/Detail views add a .detail property to the view function. + # If the view is a list view, detail will be False, and this + # check should be skipped. + if getattr(view.put, 'detail', True): + view.get_object() except (exceptions.APIException, PermissionDenied, Http404): pass else: diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 6819f1504..905f52c62 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -5,8 +5,9 @@ from django.db import models from django.test import TestCase from rest_framework import ( - exceptions, metadata, serializers, status, versioning, views + exceptions, metadata, serializers, status, versioning, views, viewsets ) +from rest_framework.decorators import list_route from rest_framework.renderers import BrowsableAPIRenderer from rest_framework.request import Request from rest_framework.test import APIRequestFactory @@ -266,6 +267,43 @@ class TestMetadata: field_info = options.get_field_info(serializers.NullBooleanField()) assert field_info['type'] == 'boolean' + def test_bug_3356_list_route_metadata(self): + class Example(models.Model): + dummy_field = models.CharField(max_length=64, default='') + + class ExampleSerializer(serializers.Serializer): + class Meta: + model = Example + + class ListRouteViewSet(viewsets.ModelViewSet): + serializer_class = ExampleSerializer + queryset = Example.objects.all() + + @list_route(methods=['GET', 'PUT']) + def put_route(self, request): + return + + put_view = ListRouteViewSet.as_view(actions={'put': 'put_route'}) + response = put_view(request) + + expected = { + 'name': 'List Route', + 'description': '', + 'renders': [ + 'application/json', + 'text/html' + ], + 'parses': [ + 'application/json', + 'application/x-www-form-urlencoded', + 'multipart/form-data' + ], + 'actions': { + 'PUT': {} + } + } + assert response.data == expected + class TestModelSerializerMetadata(TestCase): def test_read_only_primary_key_related_field(self):