From 3f56a96ae1c4f8c36a05b4112bf882045043dfe3 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 9 Apr 2019 13:26:12 +1000 Subject: [PATCH 1/5] If the model is indexed, use the search backend --- wagtailautocomplete/views.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/wagtailautocomplete/views.py b/wagtailautocomplete/views.py index 1888336..331d4dd 100644 --- a/wagtailautocomplete/views.py +++ b/wagtailautocomplete/views.py @@ -8,6 +8,8 @@ from django.http import (HttpResponseBadRequest, HttpResponseForbidden, JsonResponse) from django.views.decorators.http import require_GET, require_POST +from wagtail.search.backends import get_search_backend +from wagtail.search.index import Indexed def render_page(page): @@ -64,10 +66,18 @@ def search(request): except ValueError: return HttpResponseBadRequest() - field_name = getattr(model, 'autocomplete_search_field', 'title') - filter_kwargs = dict() - filter_kwargs[field_name + '__icontains'] = search_query - queryset = model.objects.filter(**filter_kwargs) + field_name = getattr(model, 'autocomplete_search_field', None) + if issubclass(model, Indexed): + search_backend = get_search_backend() + if field_name: + queryset = search_backend.search(search_query, model, fields=[field_name]) + else: + queryset = search_backend.search(search_query, model) + else: + field_name = field_name if field_name else 'title' + filter_kwargs = dict() + filter_kwargs[field_name + '__icontains'] = search_query + queryset = model.objects.filter(**filter_kwargs) if getattr(queryset, 'live', None): # Non-Page models like Snippets won't have a live/published status From 4a5214762ce78becb86b00585d9cf2d977189183 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 9 Apr 2019 13:38:06 +1000 Subject: [PATCH 2/5] Add pre-2.0 imports --- wagtailautocomplete/views.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/wagtailautocomplete/views.py b/wagtailautocomplete/views.py index 331d4dd..7e0c087 100644 --- a/wagtailautocomplete/views.py +++ b/wagtailautocomplete/views.py @@ -8,8 +8,14 @@ from django.http import (HttpResponseBadRequest, HttpResponseForbidden, JsonResponse) from django.views.decorators.http import require_GET, require_POST -from wagtail.search.backends import get_search_backend -from wagtail.search.index import Indexed +from wagtail import VERSION + +if VERSION > (2, 0): + from wagtail.search.backends import get_search_backend + from wagtail.search.index import Indexed +else: + from wagtail.wagtailsearch.backends import get_search_backend + from wagtail.wagtailsearch.index import Indexed def render_page(page): From c6ab641b959025b75f1b951fd7a8dd1e9c076f84 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 9 Apr 2019 13:53:44 +1000 Subject: [PATCH 3/5] Update docs --- docs/customization.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/customization.rst b/docs/customization.rst index 7a4a20d..197f165 100644 --- a/docs/customization.rst +++ b/docs/customization.rst @@ -27,7 +27,11 @@ You can enable this type of behavior by defining an ``autocomplete_create`` clas Custom Search Field =================== -By default, the autocomplete widget will match input against the ``title`` field on your model. If you're using a model that doesn't have a ``title`` attribute, or you just want to search using a different field, you can customize which field it matches against by defining an ``autocomplete_search_field`` property on your model: +If your model is indexed using the native Wagtail `indexing methods `_, then the configured backend will be used to search accross all indexed fields. +You can narrow the scope by setting the ``autocomplete_search_field`` on the model like in the example to only search one field. + +If your model is not indexed then the native Django ORM is used to match input against the ``title`` field on your model. +If you're using a model that doesn't have a ``title`` attribute, or you just want to search using a different field, you can customize which field it matches against by defining an ``autocomplete_search_field`` property on your model: .. code-block:: python From e39be43375d1931d10b401255dc2e00a8a7a13b7 Mon Sep 17 00:00:00 2001 From: Brett Haydon Date: Mon, 20 May 2019 17:23:51 +1000 Subject: [PATCH 4/5] Update views.py Add support for istartswith search on the standard backend --- wagtailautocomplete/views.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/wagtailautocomplete/views.py b/wagtailautocomplete/views.py index 7e0c087..bc8fa55 100644 --- a/wagtailautocomplete/views.py +++ b/wagtailautocomplete/views.py @@ -73,18 +73,24 @@ def search(request): return HttpResponseBadRequest() field_name = getattr(model, 'autocomplete_search_field', None) - if issubclass(model, Indexed): + custom_lookup = isinstance(field_name, (list, tuple)) + if not custom_lookup and issubclass(model, Indexed): search_backend = get_search_backend() if field_name: queryset = search_backend.search(search_query, model, fields=[field_name]) else: queryset = search_backend.search(search_query, model) else: + lookup_type = 'icontains' + if custom_lookup: + lookup_type = field_name[1] + field_name = field_name[0] field_name = field_name if field_name else 'title' filter_kwargs = dict() - filter_kwargs[field_name + '__icontains'] = search_query + filter_kwargs["{0}__{1}".format(field_name, lookup_type)] = search_query queryset = model.objects.filter(**filter_kwargs) + if getattr(queryset, 'live', None): # Non-Page models like Snippets won't have a live/published status # and thus should not be filtered with a call to `live`. From 6ab31788ed070669b3c382ac335412ec8d88d486 Mon Sep 17 00:00:00 2001 From: Brett Haydon Date: Mon, 20 May 2019 17:30:31 +1000 Subject: [PATCH 5/5] Update customization.rst Update for custom Django ORM lookups --- docs/customization.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/customization.rst b/docs/customization.rst index 197f165..ec50b68 100644 --- a/docs/customization.rst +++ b/docs/customization.rst @@ -56,6 +56,12 @@ If you're using a model that doesn't have a ``title`` attribute, or you just wan MyModel.objects.filter(my_special_field__icontains='part') Additionally, this means that ``autocomplete_search_field`` *must* be a model field and cannot be an arbitrary property or method. + +To use a different lookup behaviour on the Django ORM you can specify a tuple with the lookup type on your model's autocomplete_search_field: + + .. code-block:: python + + autocomplete_search_field = ('my_special_field', 'istartswith') Custom Label Display ====================