diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index ea150e10f..2ad0f361f 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -159,6 +159,12 @@ See the pagination documentation for further guidance on [setting the pagination --- +### DEFAULT_SEARCH_LOOKUP + +The type of lookup to use for searching if a lookup prefix is not specified. May be any of the Django search lookups (e.g. `icontains`, `istartswith`, `iexact`, `search`, `iregex`). + +Default: `icontains` + ### SEARCH_PARAM The name of a query parameter, which can be used to specify the search term used by `SearchFilter`. diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 830d0a616..a5a658891 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -42,6 +42,7 @@ class SearchFilter(BaseFilterBackend): search_param = api_settings.SEARCH_PARAM template = 'rest_framework/filters/search.html' lookup_prefixes = { + '*': 'icontains', '^': 'istartswith', '=': 'iexact', '@': 'search', @@ -63,7 +64,7 @@ class SearchFilter(BaseFilterBackend): if lookup: field_name = field_name[1:] else: - lookup = 'icontains' + lookup = api_settings.DEFAULT_SEARCH_LOOKUP return LOOKUP_SEP.join([field_name, lookup]) def must_call_distinct(self, queryset, search_fields): diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 6c581f8e8..c3ba4ba1f 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -69,6 +69,7 @@ DEFAULTS = { 'PAGE_SIZE': None, # Filtering + 'DEFAULT_SEARCH_LOOKUP': 'icontains', 'SEARCH_PARAM': 'search', 'ORDERING_PARAM': 'ordering', diff --git a/tests/test_filters.py b/tests/test_filters.py index f9e068fec..d33eec2fd 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -134,6 +134,25 @@ class SearchFilterTests(TestCase): {'id': 2, 'title': 'zz', 'text': 'bcd'} ] + def test_search_with_nonstandard_search_lookup(self): + with override_settings(REST_FRAMEWORK={'DEFAULT_SEARCH_LOOKUP': 'iregex'}): + reload_module(filters) + + class SearchListView(generics.ListAPIView): + queryset = SearchFilterModel.objects.all() + serializer_class = SearchFilterSerializer + filter_backends = (filters.SearchFilter,) + search_fields = ('title', '*text') + + view = SearchListView.as_view() + request = factory.get('/', {'search': 'z{2} b'}) + response = view(request) + assert response.data == [ + {'id': 2, 'title': 'zz', 'text': 'bcd'} + ] + + reload_module(filters) + def test_search_with_nonstandard_search_param(self): with override_settings(REST_FRAMEWORK={'SEARCH_PARAM': 'query'}): reload_module(filters)