carltongibson / django-filter

A generic system for filtering Django QuerySets based on user selections
https://django-filter.readthedocs.io/en/main/
Other
4.46k stars 769 forks source link

OpenApi documentation missing filtersets on custom actions #1487

Closed CelestialGuru closed 1 year ago

CelestialGuru commented 2 years ago

Adding filtersets to my viewset works when tested, but the generated OpenApi documenation is absent any details of the query parameters.

class RunLayoutNodeFilterSet(filterset.FilterSet):
    class Meta:
        model = models.LayoutNode
        fields = ("run",)

    id__in = NumberInFilter(field_name="id", lookup_expr="in")

class RunLayoutNodeViewSet(viewsets.ModelViewSet):
    ...
    filter_backends = (DjangoFilterBackend,)
    filterset_class = filtersets.RunLayoutNodeFilterSet

    @decorators.action(detail=False, methods=["delete"], url_path="bulk-destroy")
    def bulk_destroy(self, request, *args, **kwargs):
        """Delete items in bulk"""
        queryset = self.get_queryset()
        filtered = self.filter_queryset(queryset)
        if not self.allow_bulk_destroy(queryset, filtered):
            return Response(status=status.HTTP_400_BAD_REQUEST)
        else:
            self.perform_bulk_destroy(filtered)
            return Response(status=status.HTTP_204_NO_CONTENT)

    def allow_bulk_destroy(self, queryset, filtered):
        """
        Hook to ensure that the bulk destroy should be allowed.
        By default this checks that the destroy is only applied to filtered querysets.
        """
        return self.request.GET

    def perform_bulk_destroy(self, instances):
        for instance in instances:
            self.perform_destroy(instance)

If I curl localhost:8000/api/quote/run-layout-nodes/bulk-destroy I correctly get a 400 status code. If I curl localhost:8000/api/quote/run-layout-nodes/bulk-destroy?id__in=1, that works too.

If I go to localhost:8000/schema/swagger-ui/, and look at the documenation for the endpoint, there's nothing about filters. The regular list-view has the filters, just not this custom action.

I've tried changing the method to others like GET or whatever, still no luck.

tfranzel commented 2 years ago

Usually django-filters only make sense on endpoints that return or operate on a list. For viewsets, that is usually only thelist() method. To signal the "list" nature of your custom action, you need to provide that information via decorator:

    @extend_schema(filters=True)
    @decorators.action(detail=False, methods=["delete"], url_path="bulk-destroy")
    def bulk_destroy(self, request, *args, **kwargs):
        ...
carltongibson commented 1 year ago

As per #1432 the in-built schema support is going to be deprecated in the next version.

Rather, I recommend you migrate to use drf-spectacular, which is much more fully featured, and under active development.

https://drf-spectacular.readthedocs.io/en/latest/readme.html

If for some reason you're not able to use drf-spectacular you can copy the django_filters.rest_framework.backends.DjangoFilterBackend into your own project to customise as needed. (But use drf-spectacular.)

Thanks.