mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-22 09:36:49 +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']
|
||||
|
||||
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.
|
||||
|
||||
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-url-filter]: https://github.com/miki725/django-url-filter
|
||||
[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):
|
||||
# This field is a m2m relation so we know we need to call distinct
|
||||
return True
|
||||
else:
|
||||
# This field has a custom __ query transform but is not a relational field.
|
||||
break
|
||||
return False
|
||||
|
||||
def filter_queryset(self, request, queryset, view):
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import datetime
|
||||
from importlib import reload as reload_module
|
||||
|
||||
import django
|
||||
import pytest
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db import models
|
||||
from django.db.models import CharField, Transform
|
||||
from django.db.models.functions import Concat, Upper
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
|
@ -189,6 +191,42 @@ class SearchFilterTests(TestCase):
|
|||
|
||||
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):
|
||||
label = models.CharField(max_length=32)
|
||||
|
|
Loading…
Reference in New Issue
Block a user