I have a long list of items I am searching through with a django-autocomplete-light
field, with the autocomplete defined as such:
class OccupationAutocomplete(autocomplete_light.AutocompleteModelBase):
model = models.Occupation
search_fields=['title',]
Some job titles are alphabetically quite far through the list, like for example:
"teacher"
which come after other 'less ideal titles', like:
"Agricultural teacher", "Architecture teacher", "Building teacher", etc...
What I would like is for the "best" match, either closest or just match that starts with the text of the search, so if someone searches for "teach", they get "teacher", as it starts with the same letters, and then other less accurate matches after.
I've tried setting search_fields
with a preferred order
search_fields=['^title','title',]
but an analysis of the autocomplete code shows that the terms are all munged into one query before being returned.
How would I go about ordering this list in a more appropriate way?
In the end I had to implement a new class, this just accepts a list of dictionaries in order of 'preferred weight' and then returns results based on that.
class OrderedAutocomplete(autocomplete_light.AutocompleteModelBase):
ordered_search_fields = []
def choices_for_request(self):
"""
Return a queryset based on :py:attr:`choices` using options
:py:attr:`split_words`, :py:attr:`search_fields` and
:py:attr:`limit_choices`.
"""
assert self.choices is not None, 'choices should be a queryset'
q = self.request.GET.get('q', '')
exclude = self.request.GET.getlist('exclude')
base_split_words = self.split_words
_choices = []
for weight,opt in enumerate(self.ordered_search_fields):
if len(_choices) >= self.limit_choices:
break
search_fields = opt['search_fields']
self.split_words = opt['split_words'] or base_split_words
conditions = self._choices_for_request_conditions(q,
search_fields)
choices = self.order_choices(self.choices.filter(conditions).exclude(pk__in=exclude))
if choices.count():
_choices += list(choices)[0:self.limit_choices]
return _choices[0:self.limit_choices]
This can then be instantiated using:
class OccupationAutocomplete(OrderedAutocomplete):
model = models.Occupation
ordered_search_fields = [
{'search_fields': ['^title'], 'split_words':False},
{'search_fields': ['title'], 'split_words':True},
]
autocomplete_light.register(OccupationAutocomplete)