-
Notifications
You must be signed in to change notification settings - Fork 771
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
Bug in get_schema_fields #551
Comments
@pySilver Thanks for the report. Any chance you can reduce this down to a test case so we can see what's happening? |
@carltongibson sorry, I'm kinda overloaded right now :( Just consider the fact that def get_schema_fields(self, view):
# This is not compatible with widgets where the query param differs from the
# filter's attribute name. Notably, this includes `MultiWidget`, where query
# params will be of the format `<name>_0`, `<name>_1`, etc...
filter_class = None
if hasattr(view, 'filter_class') and view.filter_class is not None:
queryset = view.filter_class.Meta.model._default_manager.all()
try:
view.filter_class._meta.model._default_manager
except AttributeError:
pass
filter_class = self.get_filter_class(view, queryset)
return [] if not filter_class else [
coreapi.Field(name=field_name, required=False, location='query')
for field_name in filter_class().filters.keys()
] |
Correct me if I'm wrong, but I don't think nested views are a supported feature of DRF? Without additional details, it seems like the problem is that the nested view hasn't been properly initialized (ie, the view not having a |
@rpkilby nested views are supported since 2.x or even earlier. It's simply about the urlconfig and has nothing do to with DRF itself. Having urlconfig like this |
Gotcha - the viewset isn't nested in another viewset, it's a multi-argument/nested url pattern.
Do you mean the view initialized by the schema generation? The view instance is directly passed to
I'm not advocating creating a fake request object. I'm saying that the view should have been properly initialized with the request. This might have been a bug that was fixed in a more recent release. See encode/django-rest-framework#4383. Also, keep in mind that this would break the |
@rpkilby yeah I'm aware that solution I've posted earlier won't work for
It's still an issue, im using latest 3.5.2 release. @tomchristie what you think about that? Whole issue looks like method signature problem since we setup queryset just because it's required and this has no real influence on fields schema extraction itself. |
No, but it may be used during filterset creation. Without the queryset/model, you have no filterset to get fields from. The queryset model is also used as part of a sanity check. Either way, I'm not convinced that the real issue is with |
Ok, how one would create a valid request object in that case? I can't see how we can create required imaginary |
It's not clear to me why you're manually creating a request here. This should be DRF's responsibility when initializing the view. |
thats what I ment – how DRF could do that, it sounds like "impossible" |
Alright, did a little poking around the schema generation. The problem is not that the request object doesn't exist. The problem is that the view is initialized without args/kwargs (see SchemaGenerator.create_view). Without the kwargs, you don't have a valid I would do the following: def get_project(self):
try:
return Project.objects.get(id=self.kwargs.get('project_id'))
except Project.DoesNotExist:
return None
def get_queryset(self):
project = self.get_project()
if project is None:
return self.queryset.none()
return self.queryset.filter(project=project) |
yeah this is basilcally what im doing now, and it looks bad. I mean you fix working code in order to make it work with schema generator. |
I mean, what do you do now if a user inputs an invalid |
it would raise 404 on def get_queryset(self):
project = self.get_project()
if project is None:
return self.queryset.none()
if self.request and self.request.user:
self.queryset = self.queryset.filter(author=self.request.user)
return self.queryset.filter(project=project) And that would lead to useless checks all over again and again across large codebase just in order to make schema generation work... It sounds insane, taking into account that queryset has no influence on fields setup at filterset class lvl. At least not now. |
There should be no problem here. The view is initialized with the request. The request user, headers, etc... should all be there.
Checking for the existence of the parent object doesn't seem insane to me. This really seems to be the only case where this kind of check would be necessary. Additionally, you may not actually need to fetch the project instance. Wy not: def get_queryset(self):
return self.queryset.filter(project_id=self.kwargs.get('project_id'))
Except this is not correct. If using |
There's always going to be some limits to what we'll be able to do with view introspection for schema generation. Broadly I'd be in favour of us making sure we document those cases and describe how you can best handle parts of it manually when required. |
Yep - this is an issue of expectations. Users (reasonably so) expect that their views will be initialized with the The django-filter docs should probably have a note that the backend specifically calls |
Hi @tomchristie @pySilver. See #553, which adds a section to the docs about the caveat. |
@tomchristie, @rpkilby what if we some manual reflection helper that might override or even disable some inspection when user wants it? We've build really large application with DRF and we've used DRF swagger from early days and we've faced a situation that 10% of views cannot be inspected on any circumstances, they are just not fit into tooling. So at some point we end up with def openapi(self, *args, **kwargs):
return {...} # dict with manually provided signatures of this view(set). "GET" -> "resource" -> "structures" It looks like a hack but on the other hand it allows you to have 90% of api docs build from source and just 10% provided manually. This is why I've created YAML support for drf-swagger in early days. |
How is this different from the approach outlined in the Explicit schema definition section of the DRF docs? For a |
Closed by #553 Schema Generation is pretty new. Both Limitations and Patterns will emerge over time. I'm happy to look at problems and concrete suggestions but don't consider this an active issue at the moment. General strategy will be (as ever) to pull the logic back into the view, bypassing the framework code, and implementing just for the case at hand. When people have done that a few times we can see where to generalise, or, if it's just edge cases, where to document. |
I'm reopening this on the back of encode/django-rest-framework#4960 When this came up previously, we had this exchange:
i.e. We could live with conditionally checking for (e.g.) Well, from encode/django-rest-framework#4960, with the new API docs, First As per @tomchristie's comment on the issue we can catch the exception and set Then We should review the need for the When If people are using Finally Is there anything to do to help people with explicitly defining the schema? |
Fixes #551 * Avoids `get_queryset` call if `filter_class` is set * Catches error otherwise
#659 is the sort of thing I have in mind |
There is an issue at
get_schema_fields
which prevents nested views to be processed. The problem is that url named argument contains a data that is consumed by queryset is always None since you manually setup a view.Example
Root of a problem lays here:
The text was updated successfully, but these errors were encountered: