This commit is contained in:
Adam Dobrawy 2025-04-10 09:22:47 -07:00 committed by GitHub
commit 462d05ce61
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 57 additions and 4 deletions

View File

@ -196,7 +196,7 @@ When in use, the browsable API will include a `SearchFilter` control:
![Search Filter](../img/search-filter.png)
The `SearchFilter` class will only be applied if the view has a `search_fields` attribute set. The `search_fields` attribute should be a list of names of text type fields on the model, such as `CharField` or `TextField`.
The `SearchFilter` class will only be applied if `SearchFilter` class itself or the view has a `search_fields` attribute set. The `search_fields` attribute should be a list of names of text type fields on the model, such as `CharField` or `TextField`.
from rest_framework import filters
@ -247,6 +247,21 @@ To dynamically change search fields based on request content, it's possible to s
return ['title']
return super().get_search_fields(view, request)
To use use multiple filters in the same view, override `search_param` and `search_fields` attribute. Many filters can be applied simultaneously on the same view. For example, the following subclass will search on `title` if the query parameter `search_title` is used and search on `text` if the query parameter `search_title` is used:
from rest_framework import filters
class TitleSearchFilter(filters.SearchFilter):
search_param = 'search_title'
search_fields = ('$title', )
class TextSearchFilter(filters.SearchFilter):
search_param = 'search_text'
search_fields = ('$text', )
class SearchListView(generics.ListAPIView):
filter_backends = (TitleSearchFilter, TextSearchFilter)
For more details, see the [Django documentation][search-django-admin].
---

View File

@ -71,14 +71,15 @@ class SearchFilter(BaseFilterBackend):
}
search_title = _('Search')
search_description = _('A search term.')
search_fields = None
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
Search fields are obtained from the search backend / 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)
return getattr(self, 'search_fields') or getattr(view, 'search_fields', None)
def get_search_terms(self, request):
"""

View File

@ -205,6 +205,43 @@ class SearchFilterTests(TestCase):
{'id': 3, 'title': 'zzz', 'text': 'cde'}
]
def test_search_with_filter_multiple(self):
class TitleSearchFilter(filters.SearchFilter):
search_param = 'search_title'
search_fields = ('$title', )
class TextSearchFilter(filters.SearchFilter):
search_param = 'search_text'
search_fields = ('$text', )
class SearchListView(generics.ListAPIView):
queryset = SearchFilterModel.objects.all()
serializer_class = SearchFilterSerializer
filter_backends = (TitleSearchFilter, TextSearchFilter)
view = SearchListView.as_view()
request = factory.get('/', {TitleSearchFilter.search_param: r'^z{3}$'})
response = view(request)
assert response.data == [
{'id': 3, 'title': 'zzz', 'text': 'cde'}
]
request = factory.get('/', {TextSearchFilter.search_param: r'^cde$'})
response = view(request)
assert response.data == [
{'id': 3, 'title': 'zzz', 'text': 'cde'}
]
request = factory.get('/', {
TitleSearchFilter.search_param: r'^(z{3}|z{2})$',
TextSearchFilter.search_param: r'^\w{3}$'
})
response = view(request)
assert response.data == [
{'id': 2, 'title': 'zz', 'text': 'bcd'},
{'id': 3, 'title': 'zzz', 'text': 'cde'}
]
def test_search_field_with_null_characters(self):
view = generics.GenericAPIView()
request = factory.get('/?search=\0as%00d\x00f')