Repository: encode/django-rest-framework

Issue:
Annotate model fields don't work as search fields anymore
## Steps to reproduce

In a viewset set queryset to have annotated field:

```
class MyViewSet()
queryset = MyModel.objects.all().annotate(new_field=..)
search_fields = ('new_field',)
```
Perform a search using `new_field`.

## Expected behavior

Return the result filtered by `new_field`.

## Actual behavior

Exception. MyModel doesn't have field new_field.

```
....
File "/usr/local/lib/python3.6/site-packages/rest_framework/filters.py" in must_call_distinct
  79.                 field = opts.get_field(part)
....
```

The reason is SearchFilter.must_call_distinct uses model._meta to look up for the fields.

Worked fine before. The bug was introduced in v3.4.0.


Discussion:

Resolving patch:
diff --git a/rest_framework/filters.py b/rest_framework/filters.py
--- a/rest_framework/filters.py
+++ b/rest_framework/filters.py
@@ -85,6 +85,9 @@ def must_call_distinct(self, queryset, search_fields):
             opts = queryset.model._meta
             if search_field[0] in self.lookup_prefixes:
                 search_field = search_field[1:]
+            # Annotated fields do not need to be distinct
+            if isinstance(queryset, models.QuerySet) and search_field in queryset.query.annotations:
+                return False
             parts = search_field.split(LOOKUP_SEP)
             for part in parts:
                 field = opts.get_field(part)


Test patch:
diff --git a/tests/test_filters.py b/tests/test_filters.py
--- a/tests/test_filters.py
+++ b/tests/test_filters.py
@@ -5,6 +5,7 @@
 import pytest
 from django.core.exceptions import ImproperlyConfigured
 from django.db import models
+from django.db.models.functions import Concat, Upper
 from django.test import TestCase
 from django.test.utils import override_settings
 from django.utils.six.moves import reload_module
@@ -329,6 +330,38 @@ class SearchListView(generics.ListAPIView):
         assert len(response.data) == 1
 
 
+class SearchFilterAnnotatedSerializer(serializers.ModelSerializer):
+    title_text = serializers.CharField()
+
+    class Meta:
+        model = SearchFilterModel
+        fields = ('title', 'text', 'title_text')
+
+
+class SearchFilterAnnotatedFieldTests(TestCase):
+    @classmethod
+    def setUpTestData(cls):
+        SearchFilterModel.objects.create(title='abc', text='def')
+        SearchFilterModel.objects.create(title='ghi', text='jkl')
+
+    def test_search_in_annotated_field(self):
+        class SearchListView(generics.ListAPIView):
+            queryset = SearchFilterModel.objects.annotate(
+                title_text=Upper(
+                    Concat(models.F('title'), models.F('text'))
+                )
+            ).all()
+            serializer_class = SearchFilterAnnotatedSerializer
+            filter_backends = (filters.SearchFilter,)
+            search_fields = ('title_text',)
+
+        view = SearchListView.as_view()
+        request = factory.get('/', {'search': 'ABCDEF'})
+        response = view(request)
+        assert len(response.data) == 1
+        assert response.data[0]['title_text'] == 'ABCDEF'
+
+
 class OrderingFilterModel(models.Model):
     title = models.CharField(max_length=20, verbose_name='verbose title')
     text = models.CharField(max_length=100)