alanjds / drf-nested-routers

Nested Routers for Django Rest Framework
https://pypi.org/project/drf-nested-routers/
Apache License 2.0
1.66k stars 158 forks source link

`parent_lookup_kwargs` raises an attribute error in django-spectacular api docs. #311

Closed 73VW closed 10 months ago

73VW commented 1 year ago

Hello there,

I am using DRF 3.14, drf-spectacular 0.26.3 and drf-nested-routers 0.93.4.

I have tried to use the NestedViewSetMixin the following:


class PlaylistViewSet(ModelViewSet):
    permission_classes = [IsAuthenticatedOrReadOnly]
    queryset = Playlist.objects
    serializer_class = PlaylistSerializer

class PlaylistTrackListViewSet(NestedViewSetMixin, ModelViewSet):
    permission_classes = [IsAuthenticatedOrReadOnly]
    queryset = PlaylistTrackRel.objects
    serializer_class = PlaylistTrackRelSerializer
    parent_lookup_kwargs = {
        "playlist_pk": "playlist__pk",
    }

When generating the doc with django spectactular, I get the following error:

KeyError at /api/v1/schema/
'playlist_pk'

Traceback (most recent call last):
  File "/var/www/app/.venv/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/app/.venv/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/app/.venv/lib/python3.11/site-packages/django/views/decorators/csrf.py", line 56, in wrapper_view
    return view_func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/app/.venv/lib/python3.11/site-packages/django/views/generic/base.py", line 104, in view
    return self.dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/app/.venv/lib/python3.11/site-packages/rest_framework/views.py", line 509, in dispatch
    response = self.handle_exception(exc)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/app/.venv/lib/python3.11/site-packages/rest_framework/views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/app/.venv/lib/python3.11/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
    raise exc
    ^^^^^^^^^
  File "/var/www/app/.venv/lib/python3.11/site-packages/rest_framework/views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/app/.venv/lib/python3.11/site-packages/drf_spectacular/views.py", line 83, in get
    return self._get_schema_response(request)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/app/.venv/lib/python3.11/site-packages/drf_spectacular/views.py", line 91, in _get_schema_response
    data=generator.get_schema(request=request, public=self.serve_public),
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/app/.venv/lib/python3.11/site-packages/drf_spectacular/generators.py", line 268, in get_schema
    paths=self.parse(request, public),
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/app/.venv/lib/python3.11/site-packages/drf_spectacular/generators.py", line 212, in parse
    view.request = spectacular_settings.GET_MOCK_REQUEST(method, path, view, input_request)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/app/.venv/lib/python3.11/site-packages/drf_spectacular/plumbing.py", line 1167, in build_mock_request
    request = view.initialize_request(request)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/app/.venv/lib/python3.11/site-packages/rest_framework_nested/viewsets.py", line 72, in initialize_request
    querydict[parent_arg] = kwargs[url_kwarg]
                            ^^^^^^^^^^^^^^^^^

Exception Type: KeyError at /api/v1/schema/
Exception Value: 'playlist_pk'

I have tried to change initialize_request based on what's done in get_queryset in viewsets.py:


    def initialize_request(self, request, *args, **kwargs):
        """
        Adds the parent params from URL inside the children data available
        """
        request = super().initialize_request(request, *args, **kwargs)

        if getattr(self, 'swagger_fake_view', False):
            return request
        for url_kwarg, fk_filter in self._get_parent_lookup_kwargs().items():
            # fk_filter is alike 'grandparent__parent__pk'
            parent_arg = fk_filter.partition('__')[0]
            for querydict in [request.data, request.query_params]:
                with _force_mutable(querydict):
                    querydict[parent_arg] = kwargs[url_kwarg]
        return request

This seem to fix the problem.

Should I open a PR to fix this?

Thanks in advance

73VW commented 10 months ago

Has it been fixed in the meantime? My PR doesn't seem to add much now

73VW commented 10 months ago

Seems to have been merged already in another MR: https://github.com/alanjds/drf-nested-routers/pull/312#issuecomment-1799908047