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

AttributeError: 'DocumentBaseSerializer' object has no attribute 'get_document_type' #592

Closed
allinws opened this issue Nov 1, 2021 · 2 comments
Labels
enhancement New feature or request fix confirmation pending issue has been fixed and confirmation from issue reporter is pending

Comments

@allinws
Copy link

allinws commented Nov 1, 2021

Getting an error due to that my "base serializer" don't hold the functions that my "sub-serializers" (that are inheriting from the base serializer contain) - bug?

View

    from .shared import *
    from drf_spectacular.utils import extend_schema_field
    from drf_spectacular.types import OpenApiTypes


    class DocumentsSerializer(BaseSerializer):
        pk = serializers.UUIDField(source='id', format='hex_verbose')
        common_type_name = serializers.CharField(source='common-type-name')
        originator_name = serializers.CharField(source='originator-short-name')
        disclosed_at = serializers.SerializerMethodField()
        headline = serializers.CharField()
        subject_companies = ListCharField(source='subject-companies')
        document_type = serializers.CharField(source='document-type')
        publishing_status = serializers.CharField(source='publishing-status')

        @extend_schema_field(OpenApiTypes.BOOL)
        def get_disclosed_at(self, obj):
            return Helpers._get_date_time(obj['disclosed-at'])

    class DocumentBaseSerializer(BaseSerializer):
        pk = serializers.UUIDField(source='id', format='hex_verbose')
        originator = OriginatorSerializer()
        subject_companies = SubjectCompaniesSerializer(many=True,source='subject-companies')
        language_specific_content = LanguageSpecificContentSerializer(many=True, source='language-specific-content')
        is_in_use = serializers.BooleanField(source='is-in-use')
        disclosed_at = serializers.SerializerMethodField()
        document_type = serializers.SerializerMethodField()
        disabled = serializers.SerializerMethodField()
        descriptions_as_html = ListCharField(source='descriptions-as-html', required=False)
        delay_publishing_until = serializers.CharField(source='delay-publishing-until')
        publishing_type = serializers.CharField(source='publishing-type')
        version = serializers.IntegerField()

        def get_disabled(self,obj):
            return Helpers._get_disabled(obj)

        @extend_schema_field(OpenApiTypes.BOOL)
        def get_disclosed_at(self, obj):
            return Helpers._get_date_time(obj['disclosed-at'])
            

    class GetArticleOfAssociationSerializer(DocumentBaseSerializer):
        def get_document_type(self, obj):
            return 'article-of-association'


    class GetCorporateGovernanceReportSerializer(DocumentBaseSerializer):
        def get_document_type(self, obj):
            return 'corporate-governance-report'


    class GetGeneralMeetingProtocolSerializer(DocumentBaseSerializer):
        def get_document_type(self, obj):
            return 'general-meeting-protocol'


    class GetPresentationSerializer(DocumentBaseSerializer):
        def get_document_type(self, obj):
            return 'presentation'


    class GetPublicOfferDocumentSerializer(DocumentBaseSerializer):
        def get_document_type(self, obj):
            return 'public-offer-document'

Full traceback

Internal Server Error: /api/schema/
Traceback (most recent call last):
File "/home/username/project/env/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
File "/home/username/project/env/lib/python3.6/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
File "/home/username/project/env/lib/python3.6/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/username/project/env/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
File "/home/username/project/env/lib/python3.6/site-packages/django/views/generic/base.py", line 71, in view
    return self.dispatch(request, *args, **kwargs)
File "/home/username/project/env/lib/python3.6/site-packages/rest_framework/views.py", line 505, in dispatch
    response = self.handle_exception(exc)
File "/home/username/project/env/lib/python3.6/site-packages/rest_framework/views.py", line 465, in handle_exception
    self.raise_uncaught_exception(exc)
File "/home/username/project/env/lib/python3.6/site-packages/rest_framework/views.py", line 476, in raise_uncaught_exception
    raise exc
File "/home/username/project/env/lib/python3.6/site-packages/rest_framework/views.py", line 502, in dispatch
    response = handler(request, *args, **kwargs)
File "/home/username/project/env/lib/python3.6/site-packages/drf_spectacular/views.py", line 67, in get
    return self._get_schema_response(request)
File "/home/username/project/env/lib/python3.6/site-packages/drf_spectacular/views.py", line 74, in _get_schema_response
    return Response(generator.get_schema(request=request, public=self.serve_public))
File "/home/username/project/env/lib/python3.6/site-packages/drf_spectacular/generators.py", line 262, in get_schema
    paths=self.parse(request, public),
File "/home/username/project/env/lib/python3.6/site-packages/drf_spectacular/generators.py", line 234, in parse
    path, path_regex, path_prefix, method, self.registry
File "/home/username/project/env/lib/python3.6/site-packages/drf_spectacular/openapi.py", line 88, in get_operation
    operation['responses'] = self._get_response_bodies()
File "/home/username/project/env/lib/python3.6/site-packages/drf_spectacular/openapi.py", line 1117, in _get_response_bodies
    return {'200': self._get_response_for_code(response_serializers, '200')}
File "/home/username/project/env/lib/python3.6/site-packages/drf_spectacular/openapi.py", line 1173, in _get_response_for_code
    component = self.resolve_serializer(serializer, 'response')
File "/home/username/project/env/lib/python3.6/site-packages/drf_spectacular/openapi.py", line 1318, in resolve_serializer
    component.schema = self._map_serializer(serializer, direction)
File "/home/username/project/env/lib/python3.6/site-packages/drf_spectacular/openapi.py", line 766, in _map_serializer
    schema = self._map_basic_serializer(serializer, direction)
File "/home/username/project/env/lib/python3.6/site-packages/drf_spectacular/openapi.py", line 841, in _map_basic_serializer
    schema = self._map_serializer_field(field, direction)
File "/home/username/project/env/lib/python3.6/site-packages/drf_spectacular/openapi.py", line 695, in _map_serializer_field
    method = getattr(field.parent, field.method_name)
AttributeError: 'DocumentBaseSerializer' object has no attribute 'get_document_type'
@tfranzel
Copy link
Owner

tfranzel commented Nov 1, 2021

Strictly speaking, I don't think it is a bug. Sure, we should catch that error and do a fallback, but the problem remains.

Option 1:

DocumentBaseSerializer is not a valid Serializer in itself (due to missing methods). Somehow spectacular tries to resolve the DocumentBaseSerializer because its specified somewhere (maybe as serializer_class or annotation). If you remove all direct usages of DocumentBaseSerializer and replace them with the sub-serializers, the issue should disappear. Your DocumentBaseSerializer serves a "abstract base class" and should not be used directly. Spectacular only does this because it is used directly somewhere in your code.

Option 2:

If that is not what you need I would recommend something like this:

class DocumentBaseSerializer(BaseSerializer):
    ....
    document_type = serializers.SerializerMethodField()
    ...
    # the literal just just as an example. you can also do "-> str:"
    def get_document_type(self, obj) -> typing.Literal['public-offer-document', 'presentation', 'article-of-association',...]:
        raise NotImplementedError()

@tfranzel tfranzel added enhancement New feature or request fix confirmation pending issue has been fixed and confirmation from issue reporter is pending labels Nov 1, 2021
@allinws
Copy link
Author

allinws commented Nov 1, 2021

Thank you for the quick response, your second alternative worked perfectly :) Was happy to find your repo and will recommend it to others as it seems to work great.

@allinws allinws closed this as completed Nov 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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