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.