diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 1a04ad5e34..5d1f6e49a0 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -218,6 +218,13 @@ For example: By default, the search parameter is named `'search`', but this may be overridden with the `SEARCH_PARAM` setting. +To dynamically change search fields based on request content, it's possible to subclass the `SearchFilter` and override the `get_search_fields()` function. For example, the following subclass will only search on `title` if the query parameter `title_only` is in the request: + + class CustomSearchFilter(self, view, request): + if request.query_params.get('title_only'): + return ('title',) + return super(CustomSearchFilter, self).get_search_fields(view, request) + For more details, see the [Django documentation][search-django-admin]. --- diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 0627bd8c4a..53d49ae453 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -53,6 +53,14 @@ class SearchFilter(BaseFilterBackend): search_title = _('Search') search_description = _('A search term.') + def get_search_fields(self, view, request): + """ + Search fields are obtained from the view, but the request is always + passed to this method. Sub-classes can override this method to + dynamically change the search fields based on request content. + """ + return getattr(view, 'search_fields', None) + def get_search_terms(self, request): """ Search terms are set by a ?search=... query parameter, @@ -90,7 +98,7 @@ def must_call_distinct(self, queryset, search_fields): return False def filter_queryset(self, request, queryset, view): - search_fields = getattr(view, 'search_fields', None) + search_fields = self.get_search_fields(view, request) search_terms = self.get_search_terms(request) if not search_fields or not search_terms: diff --git a/tests/test_filters.py b/tests/test_filters.py index 2d4eb132e8..39a96f9945 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -156,6 +156,31 @@ class SearchListView(generics.ListAPIView): reload_module(filters) + def test_search_with_filter_subclass(self): + class CustomSearchFilter(filters.SearchFilter): + # Filter that dynamically changes search fields + def get_search_fields(self, view, request): + if request.query_params.get('title_only'): + return ('$title',) + return super(CustomSearchFilter, self).get_search_fields(view, request) + + class SearchListView(generics.ListAPIView): + queryset = SearchFilterModel.objects.all() + serializer_class = SearchFilterSerializer + filter_backends = (CustomSearchFilter,) + search_fields = ('$title', '$text') + + view = SearchListView.as_view() + request = factory.get('/', {'search': '^\w{3}$'}) + response = view(request) + assert len(response.data) == 10 + + request = factory.get('/', {'search': '^\w{3}$', 'title_only': 'true'}) + response = view(request) + assert response.data == [ + {'id': 3, 'title': 'zzz', 'text': 'cde'} + ] + class AttributeModel(models.Model): label = models.CharField(max_length=32)
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: