Repository: encode/django-rest-framework

Issue:
SchemaGenerator fails with message "object has no attribute 'get_serializer_class'"
Hi,

I just upgraded to 3.4.0 of django-rest-framework. In my application i have a API view which doesn't have a GET method implemented only POST is there.

Now i am trying to auto generate the schema using the tutorial given. Here is my code for the schema view.

```
from rest_framework.decorators import api_view, renderer_classes
from rest_framework import renderers, schemas

@api_view()
@renderer_classes([renderers.CoreJSONRenderer, ])
def schema_view(request):
    generator = schemas.SchemaGenerator(title='Bookings API')
    return generator.get_schema()
```

Now when i try to view this schema i am getting this error.

```
Traceback (most recent call last):
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/django/views/generic/base.py", line 68, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/rest_framework/views.py", line 466, in dispatch
    response = self.handle_exception(exc)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/rest_framework/views.py", line 463, in dispatch
    response = handler(request, *args, **kwargs)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/rest_framework/decorators.py", line 52, in handler
    return func(*args, **kwargs)
  File "/home/ashish/Projects/backend/oyster/config/swagger.py", line 7, in schema_view
    generator = schemas.SchemaGenerator(title='Bookings API')
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/rest_framework/schemas.py", line 74, in __init__
    self.endpoints = self.get_api_endpoints(patterns)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/rest_framework/schemas.py", line 128, in get_api_endpoints
    prefix=path_regex
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/rest_framework/schemas.py", line 121, in get_api_endpoints
    link = self.get_link(path, method, callback)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/rest_framework/schemas.py", line 196, in get_link
    fields += self.get_serializer_fields(path, method, callback, view)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/rest_framework/schemas.py", line 256, in get_serializer_fields
    serializer_class = view.get_serializer_class()
AttributeError: 'LogoutView' object has no attribute 'get_serializer_class'
```

Here is my LogoutView class..

```
class LogoutView(APIView):
    permission_classes = (AllowAny,)

    def post(self, request):
        return self.logout(request)

    def logout(self, request):
        try:
            request.user.auth_token.delete()
        except (AttributeError, ObjectDoesNotExist):
            pass

        logout(request)

        log.info('Logout Successful | %s' % request.user)
        content = {'success': settings.USERS_LOGOUT_MSG}
        return Response(content, status=status.HTTP_200_OK)
```

Am i missing something? What needs to be done to auto generate the schema?



Discussion:
I am also seeing a similar error. Except, that the call to `view.get_serializer_class` from my `ModelViewSet` gets into the `get_serializer_class` method and fails when it doesn't have the `request` attribute.

Is there any update on this one? I will try to write a failing test case ...

@geekashu 

Have you defined a serializer for this view? You should either have a `serializer_class = X` or override `get_serializer_class()` ?

The OP mentions an `APIView` and I'm not sure `SchemaGenerator` do work with them which is why I removed the bug status.

@lwm test case would definitively help get this fixed faster.

@lwm No i haven't defined a serializer for this view.

@xordoquy SchemaGenerator should work with all APIView  as per this [comment](https://github.com/tomchristie/django-rest-framework/pull/4179#discussion_r69324115)

I am getting the same error on my function based views. My view looks sort of like this

<pre>
@api_view(['GET', 'POST'])
@authentication_classes((CustomAuthentication, ))
@permission_classes((CustomPermission, ))
def get_all(request):
    return Response(CustomSerializer(CustomModel.objects.all(), many=True).data)
</pre>

Before you ask, I have to support both GET and POST for legacy reasons.

Issue is easy to re-recreate - basically when SchemaGenerator iterates through API views and any of them derived from `APIView`, simply because it does not have this method.


Resolving patch:
diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py
--- a/rest_framework/schemas.py
+++ b/rest_framework/schemas.py
@@ -111,7 +111,6 @@ def get_api_endpoints(self, patterns, prefix=''):
 
         for pattern in patterns:
             path_regex = prefix + pattern.regex.pattern
-
             if isinstance(pattern, RegexURLPattern):
                 path = self.get_path(path_regex)
                 callback = pattern.callback
@@ -253,6 +252,9 @@ def get_serializer_fields(self, path, method, callback, view):
 
         fields = []
 
+        if not (hasattr(view, 'get_serializer_class') and callable(getattr(view, 'get_serializer_class'))):
+            return []
+
         serializer_class = view.get_serializer_class()
         serializer = serializer_class()
 


Test patch:
diff --git a/tests/test_schemas.py b/tests/test_schemas.py
--- a/tests/test_schemas.py
+++ b/tests/test_schemas.py
@@ -5,8 +5,11 @@
 
 from rest_framework import filters, pagination, permissions, serializers
 from rest_framework.compat import coreapi
+from rest_framework.response import Response
 from rest_framework.routers import DefaultRouter
+from rest_framework.schemas import SchemaGenerator
 from rest_framework.test import APIClient
+from rest_framework.views import APIView
 from rest_framework.viewsets import ModelViewSet
 
 
@@ -31,11 +34,24 @@ class ExampleViewSet(ModelViewSet):
     serializer_class = ExampleSerializer
 
 
+class ExampleView(APIView):
+    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
+
+    def get(self, request, *args, **kwargs):
+        return Response()
+
+    def post(self, request, *args, **kwargs):
+        return Response()
+
+
 router = DefaultRouter(schema_title='Example API' if coreapi else None)
 router.register('example', ExampleViewSet, base_name='example')
 urlpatterns = [
     url(r'^', include(router.urls))
 ]
+urlpatterns2 = [
+    url(r'^example-view/$', ExampleView.as_view(), name='example-view')
+]
 
 
 @unittest.skipUnless(coreapi, 'coreapi is not installed')
@@ -135,3 +151,29 @@ def test_authenticated_request(self):
             }
         )
         self.assertEqual(response.data, expected)
+
+
+@unittest.skipUnless(coreapi, 'coreapi is not installed')
+class TestSchemaGenerator(TestCase):
+    def test_view(self):
+        schema_generator = SchemaGenerator(title='Test View', patterns=urlpatterns2)
+        schema = schema_generator.get_schema()
+        expected = coreapi.Document(
+            url='',
+            title='Test View',
+            content={
+                'example-view': {
+                    'create': coreapi.Link(
+                        url='/example-view/',
+                        action='post',
+                        fields=[]
+                    ),
+                    'read': coreapi.Link(
+                        url='/example-view/',
+                        action='get',
+                        fields=[]
+                    )
+                }
+            }
+        )
+        self.assertEquals(schema, expected)