diff --git a/rest_framework/filters.py b/rest_framework/filters.py index f525443b6c..4d5cc4c13a 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -122,6 +122,8 @@ def must_call_distinct(self, queryset, search_fields): continue parts = search_field.split(LOOKUP_SEP) for part in parts: + if part == "pk": + part = opts.pk.name field = opts.get_field(part) if hasattr(field, 'get_path_info'): # This field is a relation, update opts to follow the relation diff --git a/tests/test_filters.py b/tests/test_filters.py index 13756501e2..80cb0a481e 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -451,6 +451,19 @@ def test_must_call_distinct(self): ["%stitle" % prefix, "%sattribute__label" % prefix] ) + def test_must_call_distinct_with_pk_alias(self): + filter_ = filters.SearchFilter() + prefixes = [''] + list(filter_.lookup_prefixes) + for prefix in prefixes: + assert not filter_.must_call_distinct( + SearchFilterModelFk._meta, + ["%spk" % prefix] + ) + assert not filter_.must_call_distinct( + SearchFilterModelFk._meta, + ["%sattribute__pk" % prefix] + ) + def test_must_call_distinct_restores_meta_for_each_field(self): # In this test case the attribute of the fk model comes first in the # list of search fields. @@ -535,6 +548,33 @@ def test_must_call_distinct(self): ["%stitle" % prefix, "%sattributes__label" % prefix] ) + def test_must_call_distinct_with_pk_alias(self): + filter_ = filters.SearchFilter() + prefixes = [''] + list(filter_.lookup_prefixes) + for prefix in prefixes: + assert not filter_.must_call_distinct( + SearchFilterModelM2M._meta, + ["%spk" % prefix] + ) + assert filter_.must_call_distinct( + SearchFilterModelM2M._meta, + ["%sattributes__pk" % prefix] + ) + + def test_search_on_pk(self): + class SearchListView(generics.ListAPIView): + queryset = SearchFilterModelM2M.objects.all() + serializer_class = SearchFilterM2MSerializer + filter_backends = (filters.SearchFilter,) + search_fields = ('=pk',) + + pk = SearchFilterModelM2M.objects.get(title='zz').pk + view = SearchListView.as_view() + request = factory.get('/', {'search': str(pk)}) + response = view(request) + assert response.status_code == 200 + assert [item['title'] for item in response.data] == ['zz'] + class Blog(models.Model): name = models.CharField(max_length=20)