Skip to content

Commit 317174b

Browse files
andrzej3393carltongibson
authored andcommitted
Avoided calling distinct on annotated fields in SearchFilter. (#6240)
Fixes #6094
1 parent 2daf6f1 commit 317174b

File tree

2 files changed

+36
-0
lines changed

2 files changed

+36
-0
lines changed

rest_framework/filters.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ def must_call_distinct(self, queryset, search_fields):
8585
opts = queryset.model._meta
8686
if search_field[0] in self.lookup_prefixes:
8787
search_field = search_field[1:]
88+
# Annotated fields do not need to be distinct
89+
if isinstance(queryset, models.QuerySet) and search_field in queryset.query.annotations:
90+
return False
8891
parts = search_field.split(LOOKUP_SEP)
8992
for part in parts:
9093
field = opts.get_field(part)

tests/test_filters.py

Lines changed: 33 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, Upper
89
from django.test import TestCase
910
from django.test.utils import override_settings
1011
from django.utils.six.moves import reload_module
@@ -329,6 +330,38 @@ class SearchListView(generics.ListAPIView):
329330
assert len(response.data) == 1
330331

331332

333+
class SearchFilterAnnotatedSerializer(serializers.ModelSerializer):
334+
title_text = serializers.CharField()
335+
336+
class Meta:
337+
model = SearchFilterModel
338+
fields = ('title', 'text', 'title_text')
339+
340+
341+
class SearchFilterAnnotatedFieldTests(TestCase):
342+
@classmethod
343+
def setUpTestData(cls):
344+
SearchFilterModel.objects.create(title='abc', text='def')
345+
SearchFilterModel.objects.create(title='ghi', text='jkl')
346+
347+
def test_search_in_annotated_field(self):
348+
class SearchListView(generics.ListAPIView):
349+
queryset = SearchFilterModel.objects.annotate(
350+
title_text=Upper(
351+
Concat(models.F('title'), models.F('text'))
352+
)
353+
).all()
354+
serializer_class = SearchFilterAnnotatedSerializer
355+
filter_backends = (filters.SearchFilter,)
356+
search_fields = ('title_text',)
357+
358+
view = SearchListView.as_view()
359+
request = factory.get('/', {'search': 'ABCDEF'})
360+
response = view(request)
361+
assert len(response.data) == 1
362+
assert response.data[0]['title_text'] == 'ABCDEF'
363+
364+
332365
class OrderingFilterModel(models.Model):
333366
title = models.CharField(max_length=20, verbose_name='verbose title')
334367
text = models.CharField(max_length=100)

0 commit comments

Comments
 (0)