AltSchool/dynamic-rest

What is the proper place to expand filters from POST data?

Closed this issue · 2 comments

I have a need for making large filters that would create a url that is too long. I basically need to filter for around 400 ids at a time.

For example, If I was going do it with url parameters, the request would look something like this.

https://mysite.com/objects/?filter{name}=ZMYND11&filter{name}=AL713922.1&filtername}=RN7SL754P&filter{name}=Sh3d2c-ps1&filter{name}=RNA5SP298&filter{name}=Shc3&filter{name}=AL355597.1&filter{name}=DIP2C&filter{name}=GPR132&filter{name}=RRM2B&filter{name}=LACTB2....

In other situations like this, I have used a POST search instead of a GET, and built the queryset from the data in the post. As usual with DRF, it seems like there are many ways to do this...

Is there a good place in the DREST framework to inject additional filters after the url filters have been parsed?

I found dynamic_rest.viewsets.WithDynamicViewSetMixin.get_extra_filters Could this be the right place?

    def get_extra_filters(self, request):
        # Override this method to enable addition of extra filters
        # (i.e., a Q()) so custom filters can be added to the queryset without
        # running into https://code.djangoproject.com/ticket/18437
        # which, without this, would mean that filters added to the queryset
        # after this is called may not behave as expected.
        return None

I ended up using get_extra_filters:

class BaseListViewSet(
    WithDynamicViewSetMixin,
    mixins.RetrieveModelMixin,
    mixins.ListModelMixin,
    viewsets.GenericViewSet,
):
    def post(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def get_extra_filters(self, request):
        f = None
        for k, v in request.POST.lists():
            f_dict = {f"{k.replace('.', '__')}__in": v}
            q = Q(**f_dict)
            if not f:
                f = q
            else:
                f = f & q
        return f

This worked surprisingly well. The only thing I am a bit unhappy about is the lack of validation on the filter name. It would have been cool to reuse the url parameter serializer translation that DynamicFilterBackend uses, but I couldn't figure out a clean way to do it. I am also not super stoked about how I assume use of the __in operator.