Commit 8a8389bd by José Padilla Committed by GitHub

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

Fix SearchFilter to-many behavior/performance
parents 6d4d4dfd d1cfec8d
...@@ -140,12 +140,14 @@ class SearchFilter(BaseFilterBackend): ...@@ -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
......
...@@ -5,6 +5,7 @@ import unittest ...@@ -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): ...@@ -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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment