Skip to content

Commit 05556e1

Browse files
committed
Resolve #39 -- Make default search more like Django admin
Different to before we now match search terms only if all bits match or the entire statement. The last part differs from the behavior in Django admin, which does not inlcude exact machtes of the entire search term inlcude spaces. Partially revert 07054b2
1 parent c093aab commit 05556e1

File tree

2 files changed

+18
-13
lines changed

2 files changed

+18
-13
lines changed

django_select2/forms.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,14 @@
4646
:parts: 1
4747
4848
"""
49-
import re
49+
import operator
5050
import uuid
51+
from functools import reduce
5152
from itertools import chain
5253
from pickle import PicklingError # nosec
5354

5455
from django import forms
56+
from django.contrib.admin.utils import lookup_needs_distinct
5557
from django.contrib.admin.widgets import SELECT2_TRANSLATIONS
5658
from django.core import signing
5759
from django.db.models import Q
@@ -315,8 +317,6 @@ class HeavySelect2TagWidget(HeavySelect2Mixin, Select2TagWidget):
315317
class ModelSelect2Mixin:
316318
"""Widget mixin that provides attributes and methods for :class:`.AutoResponseView`."""
317319

318-
_word_split_pattern = re.compile(r"\t|\n| ")
319-
320320
model = None
321321
queryset = None
322322
search_fields = []
@@ -400,19 +400,24 @@ def filter_queryset(self, request, term, queryset=None, **dependent_fields):
400400
search_fields = self.get_search_fields()
401401
select = Q()
402402

403-
if term != "":
404-
for field in search_fields:
405-
field_select = Q(**{field: term})
406-
if "contains" in field:
407-
for word in filter(None, self._word_split_pattern.split(term)):
408-
field_select |= Q(**{field: word})
409-
410-
select |= field_select
403+
use_distinct = False
404+
if search_fields and term:
405+
for bit in term.split():
406+
or_queries = [Q(**{orm_lookup: bit}) for orm_lookup in search_fields]
407+
select &= reduce(operator.or_, or_queries)
408+
or_queries = [Q(**{orm_lookup: term}) for orm_lookup in search_fields]
409+
select |= reduce(operator.or_, or_queries)
410+
use_distinct |= any(
411+
lookup_needs_distinct(queryset.model._meta, search_spec)
412+
for search_spec in search_fields
413+
)
411414

412415
if dependent_fields:
413416
select &= Q(**dependent_fields)
414417

415-
return queryset.filter(select).distinct()
418+
if use_distinct:
419+
queryset.filter(select).distinct()
420+
return queryset.filter(select)
416421

417422
def get_queryset(self):
418423
"""

tests/test_forms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ def test_filter_queryset__contains(self, genres):
482482
assert qs.exists()
483483

484484
qs = widget.filter_queryset(None, "NOT Gen")
485-
assert qs.exists(), "contains works even if only one part matches"
485+
assert not qs.exists(), "contains works even if all bits match"
486486

487487
def test_filter_queryset__multiple_fields(self, genres):
488488
genre = Genre.objects.create(title="Space Genre")

0 commit comments

Comments
 (0)