Skip to content

when getting shcema, happy an exception #7069

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
fangaofeng opened this issue Nov 28, 2019 · 3 comments
Closed

when getting shcema, happy an exception #7069

fangaofeng opened this issue Nov 28, 2019 · 3 comments

Comments

@fangaofeng
Copy link

Checklist

version:
django 2.2
django rest framework 3.10.3

Steps to reproduce

Example code is follow:
user Autoschema

class TrainGropMemberModifyViewSet(viewsets.ModelViewSet):
    def get_serializer_class(self):
        if self.request.method == 'PATCH':
            return TrainGropListSerializer
        if self.request.method == 'PUT':
            return TrainGroupModifySerializer

Expected behavior

get shcema

Actual behavior

File "H:\trian\whlrest\traingroup\api.py", line 34, in get_serializer_class
if self.request.method == 'PATCH':
AttributeError: 'NoneType' object has no attribute 'method'

when i use drf_yasg, drf_yasg can return correct schema.

@fangaofeng
Copy link
Author

i think , when if public ,should give public but not reques =None.
And then check view permission,if public not check .

class SchemaGenerator(BaseSchemaGenerator):
    def get_schema(self, request=None, public=False):
        """
        Generate a OpenAPI schema.
        """
        self._initialise_endpoints()

        paths = self.get_paths(None if public else request)
        if not paths:
            return None

class BaseSchemaGenerator(object):
    def has_view_permissions(self, path, method, view):
        """
        Return `True` if the incoming request has the correct view permissions.
        """
        if view.request is None:
            return True

        try:
            view.check_permissions(view.request)
        except (exceptions.APIException, Http404, PermissionDenied):
            return False
        return True

maby change:

class SchemaGenerator(BaseSchemaGenerator):
    def get_schema(self, request=None, public=False):
        """
        Generate a OpenAPI schema.
        """
        self._initialise_endpoints()

        paths = self.get_paths(public,request)
        if not paths:
            return None

class BaseSchemaGenerator(object):
    def has_view_permissions(self,public, path, method, view):
        """
        Return `True` if the incoming request has the correct view permissions.
        """
        if public is True:
            return True

        try:
            view.check_permissions(view.request)
        except (exceptions.APIException, Http404, PermissionDenied):
            return False
        return True

@gnuletik
Copy link
Contributor

gnuletik commented Mar 3, 2020

I tried the following test on the master branch (commit: https://github.com/encode/django-rest-framework/tree/609f708a27bd38496b912c44742287c57e7af912):

diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py
index 35d676d6..8dafb47e 100644
--- a/tests/schemas/test_openapi.py
+++ b/tests/schemas/test_openapi.py
@@ -1070,3 +1070,15 @@ class TestGenerator(TestCase):
         assert 'components' in schema
         assert 'schemas' in schema['components']
         assert 'Duplicate' in schema['components']['schemas']
+
+    def test_conditional_serializer(self):
+        router = routers.SimpleRouter()
+        router.register('account', views.ConditionalViewSet, basename="account")
+        urlpatterns = router.urls
+
+        generator = SchemaGenerator(patterns=urlpatterns)
+
+        request = create_request('/')
+        schema = generator.get_schema(request=request)
+
+        print(schema)
diff --git a/tests/schemas/views.py b/tests/schemas/views.py
index 1c8235b4..d3eeb6a6 100644
--- a/tests/schemas/views.py
+++ b/tests/schemas/views.py
@@ -11,7 +11,7 @@ from rest_framework.decorators import action
 from rest_framework.response import Response
 from rest_framework.schemas.openapi import AutoSchema
 from rest_framework.views import APIView
-from rest_framework.viewsets import GenericViewSet
+from rest_framework.viewsets import ModelViewSet, GenericViewSet
 
 
 class ExampleListView(APIView):
@@ -215,3 +215,11 @@ class ExampleAutoSchemaDuplicate2(generics.GenericAPIView):
 
         serializer = self.get_serializer(data=now.date(), datetime=now)
         return Response(serializer.data)
+
+
+class ConditionalViewSet(ModelViewSet):
+    def get_serializer_class(self):
+        if self.request.method == 'PATCH':
+            return ExampleSerializer
+        else:
+            return ExampleSerializerModel

I just had to change the get_serializer_class condition in order to always return a serializer and it worked.

I had the following Exception if I don't always return a serializer (which makes sense).

rest_framework/schemas/openapi.py:588: in _get_serializer
    return view.get_serializer()
rest_framework/generics.py:110: in get_serializer
    return serializer_class(*args, **kwargs)
E   TypeError: 'NoneType' object is not callable

Can give another try with the master branch and create a PR with a failing test (in case it does not work)?
Thanks!

@carltongibson
Copy link
Collaborator

I just had to change the get_serializer_class condition in order to always return a serializer and it worked.

That's exactly right.

Your get_serializer() needs to work without a request.

Maybe there's a docs PR for this but...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants