Merge pull request #5264 from rpkilby/search-filter-reverse

Fix SearchFilter to-many behavior/performance
This commit is contained in:
José Padilla 2017-07-11 10:08:37 -04:00 committed by GitHub
commit 8a8389bd4b
2 changed files with 49 additions and 1 deletions

View File

@ -140,12 +140,14 @@ class SearchFilter(BaseFilterBackend):
] ]
base = queryset base = queryset
conditions = []
for search_term in search_terms: for search_term in search_terms:
queries = [ queries = [
models.Q(**{orm_lookup: search_term}) models.Q(**{orm_lookup: search_term})
for orm_lookup in orm_lookups for orm_lookup in orm_lookups
] ]
queryset = queryset.filter(reduce(operator.or_, queries)) conditions.append(reduce(operator.or_, queries))
queryset = queryset.filter(reduce(operator.and_, conditions))
if self.must_call_distinct(queryset, search_fields): if self.must_call_distinct(queryset, search_fields):
# Filtering against a many-to-many field requires us to # Filtering against a many-to-many field requires us to

View File

@ -5,6 +5,7 @@ import unittest
import warnings import warnings
from decimal import Decimal from decimal import Decimal
import django
import pytest import pytest
from django.conf.urls import url from django.conf.urls import url
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
@ -645,6 +646,51 @@ class SearchFilterM2MTests(TestCase):
) )
class Blog(models.Model):
name = models.CharField(max_length=20)
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=120)
pub_date = models.DateField(null=True)
class BlogSerializer(serializers.ModelSerializer):
class Meta:
model = Blog
fields = '__all__'
class SearchFilterToManyTests(TestCase):
@classmethod
def setUpTestData(cls):
b1 = Blog.objects.create(name='Blog 1')
b2 = Blog.objects.create(name='Blog 2')
# Multiple entries on Lennon published in 1979 - distinct should deduplicate
Entry.objects.create(blog=b1, headline='Something about Lennon', pub_date=datetime.date(1979, 1, 1))
Entry.objects.create(blog=b1, headline='Another thing about Lennon', pub_date=datetime.date(1979, 6, 1))
# Entry on Lennon *and* a separate entry in 1979 - should not match
Entry.objects.create(blog=b2, headline='Something unrelated', pub_date=datetime.date(1979, 1, 1))
Entry.objects.create(blog=b2, headline='Retrospective on Lennon', pub_date=datetime.date(1990, 6, 1))
@unittest.skipIf(django.VERSION < (1, 9), "Django 1.8 does not support transforms")
def test_multiple_filter_conditions(self):
class SearchListView(generics.ListAPIView):
queryset = Blog.objects.all()
serializer_class = BlogSerializer
filter_backends = (filters.SearchFilter,)
search_fields = ('=name', 'entry__headline', '=entry__pub_date__year')
view = SearchListView.as_view()
request = factory.get('/', {'search': 'Lennon,1979'})
response = view(request)
assert len(response.data) == 1
class OrderingFilterModel(models.Model): class OrderingFilterModel(models.Model):
title = models.CharField(max_length=20, verbose_name='verbose title') title = models.CharField(max_length=20, verbose_name='verbose title')
text = models.CharField(max_length=100) text = models.CharField(max_length=100)