Restore meta information for each search field.

The meta information stored in opts needs to be restored for
each search field. Otherwise it references the wrong model
when an attribute of a related model comes before an attribute
of the original model in search fields. This doesn't apply to
m2m relations since must_call_distinct returns True in that
case.
This commit is contained in:
Andi Albrecht 2016-07-22 11:55:17 +02:00
parent 0f61c9ec29
commit c5a2a13040
2 changed files with 42 additions and 1 deletions

View File

@ -174,8 +174,8 @@ class SearchFilter(BaseFilterBackend):
"""
Return True if 'distinct()' should be used to query the given lookups.
"""
opts = queryset.model._meta
for search_field in search_fields:
opts = queryset.model._meta
if search_field[0] in self.lookup_prefixes:
search_field = search_field[1:]
parts = search_field.split(LOOKUP_SEP)

View File

@ -454,6 +454,47 @@ class AttributeModel(models.Model):
label = models.CharField(max_length=32)
class SearchFilterModelFk(models.Model):
title = models.CharField(max_length=20)
attribute = models.ForeignKey(AttributeModel)
class SearchFilterFkSerializer(serializers.ModelSerializer):
class Meta:
model = SearchFilterModelFk
fields = '__all__'
class SearchFilterFkTests(TestCase):
def test_must_call_distinct(self):
filter_ = filters.SearchFilter()
prefixes = [''] + list(filter_.lookup_prefixes)
for prefix in prefixes:
self.assertFalse(
filter_.must_call_distinct(
SearchFilterModelFk._meta, ["%stitle" % prefix]
)
)
self.assertFalse(
filter_.must_call_distinct(
SearchFilterModelFk._meta, ["%stitle" % prefix, "%sattribute__label" % 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.
filter_ = filters.SearchFilter()
prefixes = [''] + list(filter_.lookup_prefixes)
for prefix in prefixes:
self.assertFalse(
filter_.must_call_distinct(
SearchFilterModelFk._meta, ["%sattribute__label" % prefix, "%stitle" % prefix]
)
)
class SearchFilterModelM2M(models.Model):
title = models.CharField(max_length=20)
text = models.CharField(max_length=100)