abolotnov
abolotnov

Reputation: 4332

Individual search filters for fields in django-rest-framework

Django-rest-framework will has a SearchFilter backend that will allow a single query against the searchable fields:

class OrganizationViewSet(viewsets.ModelViewSet):
    queryset = Organization.objects.all()
    serializer_class = OrganizationSerializer
    pagination_class = CustomPagination
    filter_backends = (filters.SearchFilter, DjangoFilterBackend)
    filter_fields = ('sector', 'industry', 'marketplace')
    search_fields = ('symbol',)

this way, when I query ...?search=AMZ it will only return records with a non-sensitive match in the symbol field.

If I add another element into the search_fields tuple, it will look for this same search string in both.

Is there a way to define these search fields individually that will allow me to do something like:

?search_symbol=AMZ&search_name=Good so that it looks for objects that have AMZ in symbol field and good in name field?

Upvotes: 3

Views: 2219

Answers (2)

David Ross
David Ross

Reputation: 1167

You should look into using django-filter instead. Might make your life easier.

https://django-filter.readthedocs.io/en/latest/index.html

Upvotes: 0

Alexander Collins
Alexander Collins

Reputation: 192

To achieve this you will need a custom filter backend extending the rest_framework.filters.SearchFilter class. Specifically in the rest_framework.filters.SearchFilter class there is a method get_search_terms:

    def get_search_terms(self, request):
        """
        Search terms are set by a ?search=... query parameter,
        and may be comma and/or whitespace delimited.
        """
        params = request.query_params.get(self.search_param, '')
        return params.replace(',', ' ').split()

We can override this method in our own CustomSearchFilter class to take control of how you specify the search terms within the url, for example:

class CustomSearchFilter(SearchFilter):
    search_field_prefix = "search_"

    def get_search_terms(self, request):
        # get search fields from the class
        search_fields = getattr(request.resolver_match.func.view_class, 'search_fields', list())

        params = []

        # iterate over each query paramater in the url
        for query_param in request.query_params:

            # check if query paramater is a search paramater
            if query_param.startswith(CustomSearchFilter.search_field_prefix):

                # extrapolate the field name while handling cases where <CustomSearchFilter.search_field_prefix> is in the field name
                field = CustomSearchFilter.search_field_prefix.join(
                    query_param.split(CustomSearchFilter.search_field_prefix)[1:]
                )

                # only apply search filter for fields that are in the views search_fields
                if field in search_fields:
                    params.append(request.query_params.get(query_param, ''))

        return params

Now replace the filters.SearchFilter with your new CustomSearchFilter in your views filter_backends.

Hope this helps, I've written this without testing the code myself so let me know how you go!

Upvotes: 5

Related Questions