Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

[Django 4.2]fix: swagger_auto_schema applied twice to method #552

Merged
merged 1 commit into from
Oct 5, 2023

Conversation

irtazaakram
Copy link
Contributor

Description

Fixes - AssertionError: swagger_auto_schema applied twice to method

__ ProgramListPaginationViewTests.test_404_with_invalid_pagination_arguments ___

self = <registrar.apps.api.v3.tests.test_views.ProgramListPaginationViewTests testMethod=test_404_with_invalid_pagination_arguments>

    def test_404_with_invalid_pagination_arguments(self):
        with self.assert_tracking(user=self.edx_admin, status_code=404):
>           response = self.get('programs?page_size=100&page=2', self.edx_admin)

registrar/apps/api/v3/tests/test_views.py:85: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
registrar/apps/api/tests/mixins.py:166: in get
    return self.request('get', path, user)
registrar/apps/api/tests/mixins.py:218: in request
    return getattr(self.client, method.lower())(path, **kwargs)
.tox/django42/lib/python3.8/site-packages/rest_framework/test.py:289: in get
    response = super().get(path, data=data, **extra)
.tox/django42/lib/python3.8/site-packages/rest_framework/test.py:206: in get
    return self.generic('GET', path, **r)
.tox/django42/lib/python3.8/site-packages/rest_framework/test.py:234: in generic
    return super().generic(
.tox/django42/lib/python3.8/site-packages/django/test/client.py:609: in generic
    return self.request(**r)
.tox/django42/lib/python3.8/site-packages/rest_framework/test.py:286: in request
    return super().request(**kwargs)
.tox/django42/lib/python3.8/site-packages/rest_framework/test.py:238: in request
    request = super().request(**kwargs)
.tox/django42/lib/python3.8/site-packages/django/test/client.py:891: in request
    self.check_exception(response)
.tox/django42/lib/python3.8/site-packages/django/test/client.py:738: in check_exception
    raise exc_value
.tox/django42/lib/python3.8/site-packages/django/core/handlers/exception.py:55: in inner
    response = get_response(request)
.tox/django42/lib/python3.8/site-packages/django/core/handlers/base.py:197: in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
.tox/django42/lib/python3.8/site-packages/django/views/decorators/csrf.py:56: in wrapper_view
    return view_func(*args, **kwargs)
.tox/django42/lib/python3.8/site-packages/django/views/generic/base.py:104: in view
    return self.dispatch(request, *args, **kwargs)
registrar/apps/api/mixins.py:89: in dispatch
    response = super().dispatch(*args, **kwargs)
.tox/django42/lib/python3.8/site-packages/rest_framework/views.py:509: in dispatch
    response = self.handle_exception(exc)
.tox/django42/lib/python3.8/site-packages/rest_framework/views.py:469: in handle_exception
    self.raise_uncaught_exception(exc)
.tox/django42/lib/python3.8/site-packages/rest_framework/views.py:480: in raise_uncaught_exception
    raise exc
.tox/django42/lib/python3.8/site-packages/rest_framework/views.py:506: in dispatch
    response = handler(request, *args, **kwargs)
.tox/django42/lib/python3.8/site-packages/django/utils/decorators.py:45: in _wrapper
    bound_method = dec(bound_method)
.tox/django42/lib/python3.8/site-packages/edx_api_doc_tools/view_utils.py:143: in schema_inner
    return swagger_auto_schema(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

view_method = functools.partial(<bound method ListAPIView.get of <registrar.apps.api.v3.views.ProgramListPaginationView object at 0x7f67160707f0>>)

    def decorator(view_method):
        assert not any(hm in extra_overrides for hm in APIView.http_method_names), "HTTP method names not allowed here"
        data = {
            'request_body': request_body,
            'query_serializer': query_serializer,
            'manual_parameters': manual_parameters,
            'operation_id': operation_id,
            'operation_summary': operation_summary,
            'deprecated': deprecated,
            'operation_description': operation_description,
            'security': security,
            'responses': responses,
            'filter_inspectors': list(filter_inspectors) if filter_inspectors else None,
            'paginator_inspectors': list(paginator_inspectors) if paginator_inspectors else None,
            'field_inspectors': list(field_inspectors) if field_inspectors else None,
            'tags': list(tags) if tags else None,
        }
        data = filter_none(data)
        if auto_schema is not unset:
            data['auto_schema'] = auto_schema
        data.update(extra_overrides)
        if not data:  # pragma: no cover
            # no overrides to set, no use in doing more work
            return view_method
    
        # if the method is an @action, it will have a bind_to_methods attribute, or a mapping attribute for drf>3.8
        bind_to_methods = getattr(view_method, 'bind_to_methods', [])
        mapping = getattr(view_method, 'mapping', {})
        mapping_methods = [mth for mth, name in mapping.items() if name == view_method.__name__]
        action_http_methods = bind_to_methods + mapping_methods
    
        # if the method is actually a function based view (@api_view), it will have a 'cls' attribute
        view_cls = getattr(view_method, 'cls', None)
        api_view_http_methods = [m for m in getattr(view_cls, 'http_method_names', []) if hasattr(view_cls, m)]
    
        available_http_methods = api_view_http_methods + action_http_methods
        existing_data = getattr(view_method, '_swagger_auto_schema', {})
    
        _methods = methods
        if methods or method:
            assert available_http_methods, "`method` or `methods` can only be specified on @action or @api_view views"
            assert bool(methods) != bool(method), "specify either method or methods"
            assert not isinstance(methods, str), "`methods` expects to receive a list of methods;" \
                                                 " use `method` for a single argument"
            if method:
                _methods = [method.lower()]
            else:
                _methods = [mth.lower() for mth in methods]
            assert all(mth in available_http_methods for mth in _methods), "http method not bound to view"
            assert not any(mth in existing_data for mth in _methods), "http method defined multiple times"
    
        if available_http_methods:
            # action or api_view
            assert bool(api_view_http_methods) != bool(action_http_methods), "this should never happen"
    
            if len(available_http_methods) > 1:
                assert _methods, \
                    "on multi-method api_view or action, you must specify " \
                    "swagger_auto_schema on a per-method basis using one of the `method` or `methods` arguments"
            else:
                # for a single-method view we assume that single method as the decorator target
                _methods = _methods or available_http_methods
    
            assert not any(hasattr(getattr(view_cls, mth, None), '_swagger_auto_schema') for mth in _methods), \
                "swagger_auto_schema applied twice to method"
            assert not any(mth in existing_data for mth in _methods), "swagger_auto_schema applied twice to method"
            existing_data.update((mth.lower(), data) for mth in _methods)
            view_method._swagger_auto_schema = existing_data
        else:
            assert not _methods, \
                "the methods argument should only be specified when decorating an action; " \
                "you should also ensure that you put the swagger_auto_schema decorator " \
                "AFTER (above) the _route decorator"
>           assert not existing_data, "swagger_auto_schema applied twice to method"
E           AssertionError: swagger_auto_schema applied twice to method

ProgramListPaginationView already inherits same schema from ProgramListView

Copy link
Contributor

@schenedx schenedx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve. Did you check on local machine that the swagger doc generated have explanations about paginations?

@irtazaakram
Copy link
Contributor Author

Hi @schenedx,

I've very little info on how to see exaplanations about paginations. This is the view for /api-docs I am getting for v3 locally.
image

I don't know how to test the API. If there's any testing instructions please share.

Thanks,

@schenedx
Copy link
Contributor

schenedx commented Oct 4, 2023

@irtazaakram Looks good. Feel free to merge.

@irtazaakram irtazaakram merged commit b4cda26 into master Oct 5, 2023
@irtazaakram irtazaakram deleted the fix-swagger-auto-schema-applied-twice-error branch October 5, 2023 04:44
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants