mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-22 17:47:04 +03:00
SearchFilter to support JSONField and HStoreField (#7121)
* SearchFilter to support Custom query Transforms Since Some fields support `__` as a custom Transform for query lookups we needed to update the m2m checking code to handle search_fields that contain __ that are not relationships. * Update documentation on SearchFilter to include references to JSON and HStore Fields.
This commit is contained in:
parent
13c08370e7
commit
812f254bbd
|
@ -206,6 +206,10 @@ You can also perform a related lookup on a ForeignKey or ManyToManyField with th
|
||||||
|
|
||||||
search_fields = ['username', 'email', 'profile__profession']
|
search_fields = ['username', 'email', 'profile__profession']
|
||||||
|
|
||||||
|
For [JSONField][JSONField] and [HStoreField][HStoreField] fields you can filter based on nested values within the data structure using the same double-underscore notation:
|
||||||
|
|
||||||
|
search_fields = ['data__breed', 'data__owner__other_pets__0__name']
|
||||||
|
|
||||||
By default, searches will use case-insensitive partial matches. The search parameter may contain multiple search terms, which should be whitespace and/or comma separated. If multiple search terms are used then objects will be returned in the list only if all the provided terms are matched.
|
By default, searches will use case-insensitive partial matches. The search parameter may contain multiple search terms, which should be whitespace and/or comma separated. If multiple search terms are used then objects will be returned in the list only if all the provided terms are matched.
|
||||||
|
|
||||||
The search behavior may be restricted by prepending various characters to the `search_fields`.
|
The search behavior may be restricted by prepending various characters to the `search_fields`.
|
||||||
|
@ -360,3 +364,5 @@ The [djangorestframework-word-filter][django-rest-framework-word-search-filter]
|
||||||
[django-rest-framework-word-search-filter]: https://github.com/trollknurr/django-rest-framework-word-search-filter
|
[django-rest-framework-word-search-filter]: https://github.com/trollknurr/django-rest-framework-word-search-filter
|
||||||
[django-url-filter]: https://github.com/miki725/django-url-filter
|
[django-url-filter]: https://github.com/miki725/django-url-filter
|
||||||
[drf-url-filter]: https://github.com/manjitkumar/drf-url-filters
|
[drf-url-filter]: https://github.com/manjitkumar/drf-url-filters
|
||||||
|
[HStoreField]: https://docs.djangoproject.com/en/3.0/ref/contrib/postgres/fields/#hstorefield
|
||||||
|
[JSONField]: https://docs.djangoproject.com/en/3.0/ref/contrib/postgres/fields/#jsonfield
|
|
@ -96,6 +96,9 @@ class SearchFilter(BaseFilterBackend):
|
||||||
if any(path.m2m for path in path_info):
|
if any(path.m2m for path in path_info):
|
||||||
# This field is a m2m relation so we know we need to call distinct
|
# This field is a m2m relation so we know we need to call distinct
|
||||||
return True
|
return True
|
||||||
|
else:
|
||||||
|
# This field has a custom __ query transform but is not a relational field.
|
||||||
|
break
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def filter_queryset(self, request, queryset, view):
|
def filter_queryset(self, request, queryset, view):
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import datetime
|
import datetime
|
||||||
from importlib import reload as reload_module
|
from importlib import reload as reload_module
|
||||||
|
|
||||||
|
import django
|
||||||
import pytest
|
import pytest
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models import CharField, Transform
|
||||||
from django.db.models.functions import Concat, Upper
|
from django.db.models.functions import Concat, Upper
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
|
@ -189,6 +191,42 @@ class SearchFilterTests(TestCase):
|
||||||
|
|
||||||
assert terms == ['asdf']
|
assert terms == ['asdf']
|
||||||
|
|
||||||
|
@pytest.mark.skipif(django.VERSION[:2] < (2, 2), reason="requires django 2.2 or higher")
|
||||||
|
def test_search_field_with_additional_transforms(self):
|
||||||
|
from django.test.utils import register_lookup
|
||||||
|
|
||||||
|
class SearchListView(generics.ListAPIView):
|
||||||
|
queryset = SearchFilterModel.objects.all()
|
||||||
|
serializer_class = SearchFilterSerializer
|
||||||
|
filter_backends = (filters.SearchFilter,)
|
||||||
|
search_fields = ('text__trim', )
|
||||||
|
|
||||||
|
view = SearchListView.as_view()
|
||||||
|
|
||||||
|
# an example custom transform, that trims `a` from the string.
|
||||||
|
class TrimA(Transform):
|
||||||
|
function = 'TRIM'
|
||||||
|
lookup_name = 'trim'
|
||||||
|
|
||||||
|
def as_sql(self, compiler, connection):
|
||||||
|
sql, params = compiler.compile(self.lhs)
|
||||||
|
return "trim(%s, 'a')" % sql, params
|
||||||
|
|
||||||
|
with register_lookup(CharField, TrimA):
|
||||||
|
# Search including `a`
|
||||||
|
request = factory.get('/', {'search': 'abc'})
|
||||||
|
|
||||||
|
response = view(request)
|
||||||
|
assert response.data == []
|
||||||
|
|
||||||
|
# Search excluding `a`
|
||||||
|
request = factory.get('/', {'search': 'bc'})
|
||||||
|
response = view(request)
|
||||||
|
assert response.data == [
|
||||||
|
{'id': 1, 'title': 'z', 'text': 'abc'},
|
||||||
|
{'id': 2, 'title': 'zz', 'text': 'bcd'},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class AttributeModel(models.Model):
|
class AttributeModel(models.Model):
|
||||||
label = models.CharField(max_length=32)
|
label = models.CharField(max_length=32)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user