Repository: encode/django-rest-framework

Issue:
CoreAPI integration for ViewSets ignores http_method_names limits.
## Checklist
- [x] I have verified that that issue exists against the `master` branch of Django REST framework.
           Pypi 3.5.3
- [x] I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
- [x] This is not a usage question. (Those should be directed to the [discussion group](https://groups.google.com/forum/#!forum/django-rest-framework) instead.)
- [x] This cannot be dealt with as a third party library. (We prefer new functionality to be [in the form of third party libraries](http://www.django-rest-framework.org/topics/third-party-resources/#about-third-party-packages) where possible.)
- [x] I have reduced the issue to the simplest possible case.
- [ ] I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)

## Steps to reproduce
a. Have attached project to highlight issue [Uses Python 3.5]
     /api/SampleRouteLimited/ will show single method available
     /schema/ shows all methods normal for a viewset


b. Alternatively use an existing viewset.
1. Limit viewset to a single method
      i.e., http_method_names = ['get']    
2. generate schema


## Expected behavior
Only show schema information for requested methods.

## Actual behavior
Returns information for all methods, even if not allowed.


[coreapi_test.zip](https://github.com/tomchristie/django-rest-framework/files/602205/coreapi_test.zip)



Discussion:
`http_method_names` is an internal implementation detail and is not intended a documented way to restrict which methods may be applied. I would suggest either:

* Use `ReadOnlyModelViewSet`.
* Build your viewset explicitly only including the actions you want... http://www.django-rest-framework.org/api-guide/viewsets/#custom-viewset-base-classes
* Use permission classes to restrict access.

Hope that helps move things forward.

The best place to start with questions like this is usually the discussion group rather than the issue tracker.

All the best,

  Tom
I'm having the same problem, the thing is that I'm excluding only **patch** in the ``http_method_names``. I'm still using the UpdateModelMixin but I only want the **update** not the **partial_update** method in the schema, because it's making swagger add the patch method that it's really not implemented in this view.
Reopening for further consideration.

Resolving patch:
diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py
--- a/rest_framework/schemas.py
+++ b/rest_framework/schemas.py
@@ -246,7 +246,9 @@ def get_allowed_methods(self, callback):
         Return a list of the valid HTTP methods for this endpoint.
         """
         if hasattr(callback, 'actions'):
-            return [method.upper() for method in callback.actions.keys()]
+            actions = set(callback.actions.keys())
+            http_method_names = set(callback.cls.http_method_names)
+            return [method.upper() for method in actions & http_method_names]
 
         return [
             method for method in


Test patch:
diff --git a/tests/test_schemas.py b/tests/test_schemas.py
--- a/tests/test_schemas.py
+++ b/tests/test_schemas.py
@@ -246,6 +246,11 @@ class PermissionDeniedExampleViewSet(ExampleViewSet):
     permission_classes = [DenyAllUsingPermissionDenied]
 
 
+class MethodLimitedViewSet(ExampleViewSet):
+    permission_classes = []
+    http_method_names = ['get', 'head', 'options']
+
+
 class ExampleListView(APIView):
     permission_classes = [permissions.IsAuthenticatedOrReadOnly]
 
@@ -368,6 +373,61 @@ def test_schema_for_regular_views(self):
         assert schema == expected
 
 
+@unittest.skipUnless(coreapi, 'coreapi is not installed')
+class TestSchemaGeneratorWithMethodLimitedViewSets(TestCase):
+    def setUp(self):
+        router = DefaultRouter()
+        router.register('example1', MethodLimitedViewSet, base_name='example1')
+        self.patterns = [
+            url(r'^', include(router.urls))
+        ]
+
+    def test_schema_for_regular_views(self):
+        """
+        Ensure that schema generation works for ViewSet classes
+        with method limitation by Django CBV's http_method_names attribute
+        """
+        generator = SchemaGenerator(title='Example API', patterns=self.patterns)
+        request = factory.get('/example1/')
+        schema = generator.get_schema(Request(request))
+
+        expected = coreapi.Document(
+            url='http://testserver/example1/',
+            title='Example API',
+            content={
+                'example1': {
+                    'list': coreapi.Link(
+                        url='/example1/',
+                        action='get',
+                        fields=[
+                            coreapi.Field('page', required=False, location='query', schema=coreschema.Integer(title='Page', description='A page number within the paginated result set.')),
+                            coreapi.Field('page_size', required=False, location='query', schema=coreschema.Integer(title='Page size', description='Number of results to return per page.')),
+                            coreapi.Field('ordering', required=False, location='query', schema=coreschema.String(title='Ordering', description='Which field to use when ordering the results.'))
+                        ]
+                    ),
+                    'custom_list_action': coreapi.Link(
+                        url='/example1/custom_list_action/',
+                        action='get'
+                    ),
+                    'custom_list_action_multiple_methods': {
+                        'read': coreapi.Link(
+                            url='/example1/custom_list_action_multiple_methods/',
+                            action='get'
+                        )
+                    },
+                    'read': coreapi.Link(
+                        url='/example1/{id}/',
+                        action='get',
+                        fields=[
+                            coreapi.Field('id', required=True, location='path', schema=coreschema.String())
+                        ]
+                    )
+                }
+            }
+        )
+        assert schema == expected
+
+
 @unittest.skipUnless(coreapi, 'coreapi is not installed')
 class TestSchemaGeneratorWithRestrictedViewSets(TestCase):
     def setUp(self):