Skip to content

Commit

Permalink
Resolve #39 -- Make default search more like Django admin
Browse files Browse the repository at this point in the history
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
  • Loading branch information
codingjoe committed Mar 9, 2021
1 parent c093aab commit 05556e1
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 13 deletions.
29 changes: 17 additions & 12 deletions django_select2/forms.py
Expand Up @@ -46,12 +46,14 @@
:parts: 1
"""
import re
import operator
import uuid
from functools import reduce
from itertools import chain
from pickle import PicklingError # nosec

from django import forms
from django.contrib.admin.utils import lookup_needs_distinct
from django.contrib.admin.widgets import SELECT2_TRANSLATIONS
from django.core import signing
from django.db.models import Q
Expand Down Expand Up @@ -315,8 +317,6 @@ class HeavySelect2TagWidget(HeavySelect2Mixin, Select2TagWidget):
class ModelSelect2Mixin:
"""Widget mixin that provides attributes and methods for :class:`.AutoResponseView`."""

_word_split_pattern = re.compile(r"\t|\n| ")

model = None
queryset = None
search_fields = []
Expand Down Expand Up @@ -400,19 +400,24 @@ def filter_queryset(self, request, term, queryset=None, **dependent_fields):
search_fields = self.get_search_fields()
select = Q()

if term != "":
for field in search_fields:
field_select = Q(**{field: term})
if "contains" in field:
for word in filter(None, self._word_split_pattern.split(term)):
field_select |= Q(**{field: word})

select |= field_select
use_distinct = False
if search_fields and term:
for bit in term.split():
or_queries = [Q(**{orm_lookup: bit}) for orm_lookup in search_fields]
select &= reduce(operator.or_, or_queries)
or_queries = [Q(**{orm_lookup: term}) for orm_lookup in search_fields]
select |= reduce(operator.or_, or_queries)
use_distinct |= any(
lookup_needs_distinct(queryset.model._meta, search_spec)
for search_spec in search_fields
)

if dependent_fields:
select &= Q(**dependent_fields)

return queryset.filter(select).distinct()
if use_distinct:
queryset.filter(select).distinct()
return queryset.filter(select)

def get_queryset(self):
"""
Expand Down
2 changes: 1 addition & 1 deletion tests/test_forms.py
Expand Up @@ -482,7 +482,7 @@ def test_filter_queryset__contains(self, genres):
assert qs.exists()

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

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

0 comments on commit 05556e1

Please sign in to comment.