Skip to content

Commit d110454

Browse files
allanbreyescarltongibson
authored andcommitted
Added SearchFilter.get_search_fields() hook. (#6279)
1 parent 1ece516 commit d110454

File tree

3 files changed

+41
-1
lines changed

3 files changed

+41
-1
lines changed

docs/api-guide/filtering.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,13 @@ For example:
218218

219219
By default, the search parameter is named `'search`', but this may be overridden with the `SEARCH_PARAM` setting.
220220

221+
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:
222+
223+
class CustomSearchFilter(self, view, request):
224+
if request.query_params.get('title_only'):
225+
return ('title',)
226+
return super(CustomSearchFilter, self).get_search_fields(view, request)
227+
221228
For more details, see the [Django documentation][search-django-admin].
222229

223230
---

rest_framework/filters.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ class SearchFilter(BaseFilterBackend):
5353
search_title = _('Search')
5454
search_description = _('A search term.')
5555

56+
def get_search_fields(self, view, request):
57+
"""
58+
Search fields are obtained from the view, but the request is always
59+
passed to this method. Sub-classes can override this method to
60+
dynamically change the search fields based on request content.
61+
"""
62+
return getattr(view, 'search_fields', None)
63+
5664
def get_search_terms(self, request):
5765
"""
5866
Search terms are set by a ?search=... query parameter,
@@ -90,7 +98,7 @@ def must_call_distinct(self, queryset, search_fields):
9098
return False
9199

92100
def filter_queryset(self, request, queryset, view):
93-
search_fields = getattr(view, 'search_fields', None)
101+
search_fields = self.get_search_fields(view, request)
94102
search_terms = self.get_search_terms(request)
95103

96104
if not search_fields or not search_terms:

tests/test_filters.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,31 @@ class SearchListView(generics.ListAPIView):
156156

157157
reload_module(filters)
158158

159+
def test_search_with_filter_subclass(self):
160+
class CustomSearchFilter(filters.SearchFilter):
161+
# Filter that dynamically changes search fields
162+
def get_search_fields(self, view, request):
163+
if request.query_params.get('title_only'):
164+
return ('$title',)
165+
return super(CustomSearchFilter, self).get_search_fields(view, request)
166+
167+
class SearchListView(generics.ListAPIView):
168+
queryset = SearchFilterModel.objects.all()
169+
serializer_class = SearchFilterSerializer
170+
filter_backends = (CustomSearchFilter,)
171+
search_fields = ('$title', '$text')
172+
173+
view = SearchListView.as_view()
174+
request = factory.get('/', {'search': '^\w{3}$'})
175+
response = view(request)
176+
assert len(response.data) == 10
177+
178+
request = factory.get('/', {'search': '^\w{3}$', 'title_only': 'true'})
179+
response = view(request)
180+
assert response.data == [
181+
{'id': 3, 'title': 'zzz', 'text': 'cde'}
182+
]
183+
159184

160185
class AttributeModel(models.Model):
161186
label = models.CharField(max_length=32)

0 commit comments

Comments
 (0)