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

Make it easier to override kwargs for the filter class (to facilitate filtering of POST requests, etc.) #911

Closed
espenak opened this issue May 18, 2018 · 1 comment

Comments

@espenak
Copy link

espenak commented May 18, 2018

Our use-case for this is to get filter data from request.data instead of request.query_params so that we can use django-filter for POST requests too. The reason why we need this is that we have a couple of APIs where the URL for the GET requests may (in extreme cases) exceed the max length for a URL in some browsers (around 2000 characters).

We have solved this with a custom subclass of DjangoFilterBackend like this:

class CustomDjangoFilterBackend(django_filters.rest_framework.DjangoFilterBackend):
    """
    Custom DjangoFilterBackend that makes it easy to override the kwargs for
    the ``filter_class``.
    """
    def get_filter_class_data(self, request):
        """
        Get the ``data`` argument for the ``filter_class`` constructor.

        Used by :meth:`.get_filter_class_kwargs`.

        Args:
            request (django.http.HttpRequest): Django HTTP request object.

        Returns:
            dict: A dict-like object, such as a QueryDict or just a dict.
        """
        return request.query_params

    def get_filter_class_kwargs(self, request, queryset):
        """
        Get kwargs for the ``filter_class`` constructor.

        Args:
            request (django.http.HttpRequest): Django HTTP request object.

        Returns:
            dict: The kwargs for ``filter_class``.
        """
        return {
            'queryset': queryset,
            'request': request,
            'data': self.get_filter_class_data(request=request)
        }

    def filter_queryset(self, request, queryset, view):
        filter_class = self.get_filter_class(view, queryset)
        if filter_class:
            filterset = filter_class(**self.get_filter_class_kwargs(request=request, queryset=queryset))
            return filterset.qs
        return queryset

And a subclass of that class for the APIs that allow filters to be received as POST data:

class CustomDjangoFilterBackendPostData(CustomDjangoFilterBackend):
    """
    Works just like ``CustomDjangoFilterBackend`` for GET request, but
    gets filter data from ``request.data`` in  ``POST`` requests.

    We use this in the (very few) APIs where the querystring may become
    larger than the max length for some browsers (~2000 chars).
    """

    def get_filter_class_data(self, request):
        """
        Add support for getting data from ``request.data`` for ``POST`` requests.
        """
        if request.method == 'POST':
            return request.data
        return super().get_filter_class_data(request=request)

I think this would be something to consider including in the django-filter codebase. I can see other use-cases too, such as forwarning arguments from request.session or other middleware data into the filters.

@carltongibson
Copy link
Owner

Closed by #865

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

No branches or pull requests

2 participants