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

SchemaGenerator doesn't pass request attribute #4278

Closed
decentral1se opened this issue Jul 18, 2016 · 11 comments
Closed

SchemaGenerator doesn't pass request attribute #4278

decentral1se opened this issue Jul 18, 2016 · 11 comments
Labels
Milestone

Comments

@decentral1se
Copy link
Contributor

Steps to reproduce

Override get_serializer_class for any view that inherits from GenericAPIView and attempt to access the request attribute when using the new SchemaGenerator, or going through DefaultRouter.

Expected behavior

The request attribute is passed into SchemaGenerator initialisation and subsequent methods have access to it.

Actual behavior

AttributeError: '<view-name>' object has no attribute 'request'

@decentral1se
Copy link
Contributor Author

I was trying to write a test case for #4265 and ended up here.

@ghost
Copy link

ghost commented Jul 28, 2016

Worth adding this also applies to the kwargs attribute.

@geekashu
Copy link

geekashu commented Aug 2, 2016

Happening for me as well for 'kwargs'.

@tomchristie
Copy link
Member

@rainyday @geekashu Not 100% clear what you folks are referring too. This?... #4359

@geekashu
Copy link

geekashu commented Aug 5, 2016

@tomchristie Following is my view code for UserDetailView

class UserDetailView(RetrieveUpdateAPIView):
    def get_serializer_class(self):
        if (self.get_object() == self.request.user) or self.request.user.is_staff:
            return UserDetailSerializer
        else:
            return UserPublicDetailSerializer

    permission_classes = (IsProfileOwnerOrReadOnly,)

    def get_object(self):
        pk = self.kwargs.get('pk', None)
        if not pk or (pk == str(self.request.user.pk)):
            obj = self.request.user
        else:
            try:
                obj = get_object_or_404(User, id=pk)
            except ValueError:
                obj = get_object_or_404(User, username=pk)

        self.check_object_permissions(self.request, obj)
        return obj

Schema generation is failing with following stacktrace...

Traceback (most recent call last):
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/django/views/generic/base.py", line 68, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/rest_framework/views.py", line 466, in dispatch
    response = self.handle_exception(exc)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/rest_framework/views.py", line 463, in dispatch
    response = handler(request, *args, **kwargs)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/rest_framework/decorators.py", line 52, in handler
    return func(*args, **kwargs)
  File "/home/ashish/Projects/backend/demoproject/config/swagger.py", line 18, in schema_view
    generator = schemas.SchemaGenerator(title='DemoProject APIs')
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/rest_framework/schemas.py", line 78, in __init__
    self.endpoints = self.get_api_endpoints(patterns)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/rest_framework/schemas.py", line 132, in get_api_endpoints
    prefix=path_regex
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/rest_framework/schemas.py", line 125, in get_api_endpoints
    link = self.get_link(path, method, callback)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/rest_framework/schemas.py", line 202, in get_link
    fields += self.get_serializer_fields(path, method, callback, view)
  File "/home/ashish/Env/backend/lib/python3.4/site-packages/rest_framework/schemas.py", line 268, in get_serializer_fields
    serializer_class = view.get_serializer_class()
  File "/home/ashish/Projects/backend/demoproject/users/views/user.py", line 35, in get_serializer_class
    if (self.get_object() == self.request.user) or self.request.user.is_staff:
  File "/home/ashish/Projects/backend/demoproject/users/views/user.py", line 43, in get_object
    pk = self.kwargs.get('pk', None)
AttributeError: 'UserDetailView' object has no attribute 'kwargs'

I think this may be related with issue described above. Schema generation is not working for me even if request attribute is not passed. I did checked with latest build i.e 3.4.3.

@mheppner
Copy link

mheppner commented Aug 7, 2016

I'm getting the same issue as @geekashu about not having self.kwargs available. Same problem with self.action when dynamically switching serializers in get_serializer_class().

@kevin-brown
Copy link
Member

Not 100% clear what you folks are referring too

To be slightly more specific as to what the issue is (we're now running into it)...

The schema generator is attempting to create a view which it can use for introspection, but the view is missing some of the attributes that we've come to expect when using generic method. One of those attributes is request, which usually points to the current request that is being processed by the view. A major use case for looking at view.request is to get the current user, view.request.user, so that the view processing logic can be switched based on who is making the request.

It's also been noted that view.action is also not set, which is the recommended way to change the serializer used within a view based on the action that is being taken. So having a different serializer for list vs detail views.

@alexgavrisco
Copy link

My issue is partially related to this one.
We have similar issue - we have custom permission system for nested routes which also is based on view's kwargs. And currently all those views are ignored by the schema generator.
Basically there are 2 problems:

  • request passed to the permission class is original request (to documentation endpoint), so it has same path and method for all views. So, our fallback for permissions which generates action from path and method doesn't work. For some views we can use generic viewset + mixins instead of generic views and this will solve this problem partially.
  • nested endpoints, like /parent-resource/<id>/resource/, don't work as well. In our case, because dispatch method (which sets some view's attr) of the view isn't invoked. And view's kwargs is empty. I understand that kwargs can't contain any values when inspecting views, but in this case all those endpoints are just ignored (since permissions return false).

Is there a correct/supported way to show those endpoints in generated documentation?

@carltongibson
Copy link
Collaborator

@Alexx-G From the sound of it, it looks like you'd need to be providing a manual specified schema, or at least create an AutoSchema subclass that knows how to fill in the blanks. Set the right path on the request; add missing attrs, etc.

Maybe you could override SchemaGenerator.create_view() to provide more details — but again, exactly what you'd need to provide there isn't clear from your example. (Personally I'd prefer whatever adjustments to the view are needed the AutoSchema inspector, rather than the generator, but, as ever, you could do it in either place.)

In general, it's difficult to see how we can take out the path parameters (say). Whatever logic you have for schema generation is going to need to be handle that.

@alexgavrisco
Copy link

@carltongibson Thank you for the answer.
Yep, after spending some time in DRF docs/sources I reached same conclusion.
I think a custom AutoSchema would work in my case.
Is there a way to apply a custom AutoSchema class to all views (just as DEFAULT_* settings)? I didn't find anything similar in docs.

@carltongibson
Copy link
Collaborator

You’ll need to create a base view class (or mixin maybe) if you want to use it universally

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

No branches or pull requests

7 participants