Skip to content

Commit 8a8389b

Browse files
authored
Merge pull request encode#5264 from rpkilby/search-filter-reverse
Fix SearchFilter to-many behavior/performance
2 parents 6d4d4df + d1cfec8 commit 8a8389b

File tree

2 files changed

+49
-1
lines changed

2 files changed

+49
-1
lines changed

rest_framework/filters.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,14 @@ def filter_queryset(self, request, queryset, view):
140140
]
141141

142142
base = queryset
143+
conditions = []
143144
for search_term in search_terms:
144145
queries = [
145146
models.Q(**{orm_lookup: search_term})
146147
for orm_lookup in orm_lookups
147148
]
148-
queryset = queryset.filter(reduce(operator.or_, queries))
149+
conditions.append(reduce(operator.or_, queries))
150+
queryset = queryset.filter(reduce(operator.and_, conditions))
149151

150152
if self.must_call_distinct(queryset, search_fields):
151153
# Filtering against a many-to-many field requires us to

tests/test_filters.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import warnings
66
from decimal import Decimal
77

8+
import django
89
import pytest
910
from django.conf.urls import url
1011
from django.core.exceptions import ImproperlyConfigured
@@ -645,6 +646,51 @@ def test_must_call_distinct(self):
645646
)
646647

647648

649+
class Blog(models.Model):
650+
name = models.CharField(max_length=20)
651+
652+
653+
class Entry(models.Model):
654+
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
655+
headline = models.CharField(max_length=120)
656+
pub_date = models.DateField(null=True)
657+
658+
659+
class BlogSerializer(serializers.ModelSerializer):
660+
class Meta:
661+
model = Blog
662+
fields = '__all__'
663+
664+
665+
class SearchFilterToManyTests(TestCase):
666+
667+
@classmethod
668+
def setUpTestData(cls):
669+
b1 = Blog.objects.create(name='Blog 1')
670+
b2 = Blog.objects.create(name='Blog 2')
671+
672+
# Multiple entries on Lennon published in 1979 - distinct should deduplicate
673+
Entry.objects.create(blog=b1, headline='Something about Lennon', pub_date=datetime.date(1979, 1, 1))
674+
Entry.objects.create(blog=b1, headline='Another thing about Lennon', pub_date=datetime.date(1979, 6, 1))
675+
676+
# Entry on Lennon *and* a separate entry in 1979 - should not match
677+
Entry.objects.create(blog=b2, headline='Something unrelated', pub_date=datetime.date(1979, 1, 1))
678+
Entry.objects.create(blog=b2, headline='Retrospective on Lennon', pub_date=datetime.date(1990, 6, 1))
679+
680+
@unittest.skipIf(django.VERSION < (1, 9), "Django 1.8 does not support transforms")
681+
def test_multiple_filter_conditions(self):
682+
class SearchListView(generics.ListAPIView):
683+
queryset = Blog.objects.all()
684+
serializer_class = BlogSerializer
685+
filter_backends = (filters.SearchFilter,)
686+
search_fields = ('=name', 'entry__headline', '=entry__pub_date__year')
687+
688+
view = SearchListView.as_view()
689+
request = factory.get('/', {'search': 'Lennon,1979'})
690+
response = view(request)
691+
assert len(response.data) == 1
692+
693+
648694
class OrderingFilterModel(models.Model):
649695
title = models.CharField(max_length=20, verbose_name='verbose title')
650696
text = models.CharField(max_length=100)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy