Skip to content
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

Nested ListSerializer throw AttributeError: 'ListSerializer' object has no attribute 'fields' #539

Closed
tui95 opened this issue Sep 29, 2021 · 5 comments
Labels
bug Something isn't working enhancement New feature or request fix confirmation pending issue has been fixed and confirmation from issue reporter is pending

Comments

@tui95
Copy link

tui95 commented Sep 29, 2021

Describe the bug
I've just changed from drf-yasg to drf-spectacular and I encounter an error when I use nested ListSerializer.

Internal Server Error: /backend/schema/
Traceback (most recent call last):
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
    raise exc
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/rest_framework/views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/drf_spectacular/views.py", line 67, in get
    return self._get_schema_response(request)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/drf_spectacular/views.py", line 74, in _get_schema_response
    return Response(generator.get_schema(request=request, public=self.serve_public))
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/drf_spectacular/generators.py", line 259, in get_schema
    paths=self.parse(request, public),
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/drf_spectacular/generators.py", line 233, in parse
    operation = view.schema.get_operation(
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/drf_spectacular/utils.py", line 281, in get_operation
    return super().get_operation(path, path_regex, path_prefix, method, registry)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 86, in get_operation
    operation['responses'] = self._get_response_bodies()
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 1051, in _get_response_bodies
    return {'200': self._get_response_for_code(response_serializers, '200')}
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 1096, in _get_response_for_code
    component = self.resolve_serializer(serializer, 'response')
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 1244, in resolve_serializer
    component.schema = self._map_serializer(serializer, direction)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 731, in _map_serializer
    schema = self._map_basic_serializer(serializer, direction)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 801, in _map_basic_serializer
    schema = self._map_serializer_field(field, direction)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 516, in _map_serializer_field
    component = self.resolve_serializer(field.child, direction)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 1244, in resolve_serializer
    component.schema = self._map_serializer(serializer, direction)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 731, in _map_serializer
    schema = self._map_basic_serializer(serializer, direction)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/drf_spectacular/openapi.py", line 795, in _map_basic_serializer
    for field in serializer.fields.values():
AttributeError: 'ListSerializer' object has no attribute 'fields'

To Reproduce
Serializers and view method are shown below

class XSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField()

class XNestedListSerializer(serializers.Serializer):
    nested_xs = serializers.ListSerializer(
        child=XSerializer(many=True),
    )

class XAPIView(generics.GenericAPIView):

    @extend_schema(
        responses=XNestedListSerializer(),
    )
    def get(self, request, *args, **kwargs):
        pass

Expected behavior
AttributeError: 'ListSerializer' object has no attribute 'fields' should not be thrown and swagger UI should load as normal.

@tui95 tui95 changed the title Nested list serializer throw AttributeError: 'ListSerializer' object has no attribute 'fields' Nested ListSerializer throw AttributeError: 'ListSerializer' object has no attribute 'fields' Sep 29, 2021
@tfranzel tfranzel added the bug Something isn't working label Sep 29, 2021
@tfranzel
Copy link
Owner

hi @tui95, amazing how this simple construction fails. i wonder why this has not come up before. people do a lot more complicated things with spectacular. i'll look at it today.

@tui95
Copy link
Author

tui95 commented Sep 29, 2021

@tfranzel Thank you for the fast response.

tfranzel added a commit that referenced this issue Sep 30, 2021
@tfranzel tfranzel added enhancement New feature or request fix confirmation pending issue has been fixed and confirmation from issue reporter is pending labels Sep 30, 2021
@tui95
Copy link
Author

tui95 commented Sep 30, 2021

Hi @tfranzel, I've tried to use the latest drf-spectacular from github, but another problem has occur. I've noticed that I declared nested list serializer by using ListField instead of ListSerializer.

class XSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField()

class XNestedListSerializer(serializers.Serializer):
    nested_xs = serializers.ListField(  # <-- change from ListSerializer
        child=XSerializer(many=True)
    )

class XAPIView(generics.GenericAPIView):

    @extend_schema(
        responses=XNestedListSerializer(),
    )
    def get(self, request, *args, **kwargs):
        pass

Here's a stack trace of the error.

Internal Server Error: /backend/schema/
Traceback (most recent call last):
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
    raise exc
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/lib/python3.8/site-packages/rest_framework/views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/src/drf-spectacular/drf_spectacular/views.py", line 67, in get
    return self._get_schema_response(request)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/src/drf-spectacular/drf_spectacular/views.py", line 74, in _get_schema_response
    return Response(generator.get_schema(request=request, public=self.serve_public))
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/src/drf-spectacular/drf_spectacular/generators.py", line 259, in get_schema
    paths=self.parse(request, public),
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/src/drf-spectacular/drf_spectacular/generators.py", line 233, in parse
    operation = view.schema.get_operation(
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/src/drf-spectacular/drf_spectacular/utils.py", line 290, in get_operation
    return super().get_operation(path, path_regex, path_prefix, method, registry)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/src/drf-spectacular/drf_spectacular/openapi.py", line 86, in get_operation
    operation['responses'] = self._get_response_bodies()
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/src/drf-spectacular/drf_spectacular/openapi.py", line 1085, in _get_response_bodies
    return {'200': self._get_response_for_code(response_serializers, '200')}
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/src/drf-spectacular/drf_spectacular/openapi.py", line 1139, in _get_response_for_code
    component = self.resolve_serializer(serializer, 'response')
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/src/drf-spectacular/drf_spectacular/openapi.py", line 1283, in resolve_serializer
    component.schema = self._map_serializer(serializer, direction)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/src/drf-spectacular/drf_spectacular/openapi.py", line 744, in _map_serializer
    schema = self._map_basic_serializer(serializer, direction)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/src/drf-spectacular/drf_spectacular/openapi.py", line 815, in _map_basic_serializer
    schema = self._map_serializer_field(field, direction)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/src/drf-spectacular/drf_spectacular/openapi.py", line 586, in _map_serializer_field
    component = self.resolve_serializer(field.child, direction)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/src/drf-spectacular/drf_spectacular/openapi.py", line 1270, in resolve_serializer
    assert_basic_serializer(serializer)
  File "/Users/tui/Library/Caches/pypoetry/virtualenvs/s1-eia-backend-fr4cJwNz-py3.8/src/drf-spectacular/drf_spectacular/plumbing.py", line 129, in assert_basic_serializer
    assert is_basic_serializer(serializer), (
AssertionError: internal assumption violated because we expected a basic serializer here and instead got a "XSerializer(many=True):
    id = IntegerField()
    name = CharField()". This may be the result of another app doing some unexpected magic or an invalid internal call. Feel free to report this as a bug at https://github.com/tfranzel/drf-spectacular/issues

tfranzel added a commit that referenced this issue Sep 30, 2021
@tfranzel
Copy link
Owner

i missed one case. that should hopefully do it

@tui95
Copy link
Author

tui95 commented Sep 30, 2021

@tfranzel Thank you. I've checked and everything works as expected.

@tfranzel tfranzel closed this as completed Oct 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working enhancement New feature or request fix confirmation pending issue has been fixed and confirmation from issue reporter is pending
Projects
None yet
Development

No branches or pull requests

2 participants