from dal import autocomplete from django.db.models import Q class ModelSearch: """Basic search engine for models based on filtering. Subclasses should override the ``model`` class attribute and specify the list of search fields to be searched in. """ model = None search_fields = [] def get_queryset_filter(self, keywords): filter_q = Q() if not keywords: return filter_q for keyword in keywords: kw_filter = Q() for field in self.search_fields: kw_filter |= Q(**{"{}__icontains".format(field): keyword}) filter_q &= kw_filter return filter_q def search(self, keywords): """Returns the queryset of model instances matching all the keywords. The semantic of the search is the following: a model instance appears in the search results iff all of the keywords given as arguments occur in at least one of the search fields. """ return self.model.objects.filter(self.get_queryset_filter(keywords)) class Select2QuerySetView(ModelSearch, autocomplete.Select2QuerySetView): """Compatibility layer between ModelSearch and Select2QuerySetView.""" def get_queryset(self): keywords = self.q.split() return super().search(keywords)