Skip to content

Commit eadeed2

Browse files
committed
Allows searching with SearchFilter in annotated fields
Fixes #6094
1 parent dd19a44 commit eadeed2

File tree

2 files changed

+33
-0
lines changed

2 files changed

+33
-0
lines changed

rest_framework/filters.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ def must_call_distinct(self, queryset, search_fields):
7979
search_field = search_field[1:]
8080
parts = search_field.split(LOOKUP_SEP)
8181
for part in parts:
82+
if isinstance(queryset, models.QuerySet) and part in queryset.query.annotations:
83+
# This field is annotated
84+
continue
8285
field = opts.get_field(part)
8386
if hasattr(field, 'get_path_info'):
8487
# This field is a relation, update opts to follow the relation

tests/test_filters.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import pytest
66
from django.core.exceptions import ImproperlyConfigured
77
from django.db import models
8+
from django.db.models.functions import Concat
89
from django.test import TestCase
910
from django.test.utils import override_settings
1011
from django.utils.six.moves import reload_module
@@ -304,6 +305,35 @@ class SearchListView(generics.ListAPIView):
304305
assert len(response.data) == 1
305306

306307

308+
class SearchFilterAnnotatedSerializer(serializers.ModelSerializer):
309+
title_text = serializers.CharField()
310+
311+
class Meta:
312+
model = SearchFilterModel
313+
fields = ('title', 'text', 'title_text')
314+
315+
316+
class SearchFilterAnnotatedFieldTests(TestCase):
317+
@classmethod
318+
def setUpTestData(cls):
319+
SearchFilterModel.objects.create(title='abc', text='def')
320+
SearchFilterModel.objects.create(title='ghi', text='jkl')
321+
322+
def test_search_in_annotated_field(self):
323+
class SearchListView(generics.ListAPIView):
324+
queryset = SearchFilterModel.objects.annotate(
325+
title_text=Concat(models.F('title'), models.F('text'), output_field=models.CharField())).all()
326+
serializer_class = SearchFilterAnnotatedSerializer
327+
filter_backends = (filters.SearchFilter,)
328+
search_fields = ('title_text',)
329+
330+
view = SearchListView.as_view()
331+
request = factory.get('/', {'search': 'abcdef'})
332+
response = view(request)
333+
assert len(response.data) == 1
334+
assert response.data[0]['title_text'] == 'abcdef'
335+
336+
307337
class OrderingFilterModel(models.Model):
308338
title = models.CharField(max_length=20, verbose_name='verbose title')
309339
text = models.CharField(max_length=100)

0 commit comments

Comments
 (0)