From 7f86cffff61b16e1aa6f7f08662d671936fa4282 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 12 Oct 2022 15:12:26 -0400 Subject: [PATCH] Extend search() to accept a lookup type --- netbox/netbox/search/__init__.py | 10 +++++++--- netbox/netbox/search/backends.py | 15 ++++++++++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/netbox/netbox/search/__init__.py b/netbox/netbox/search/__init__.py index b15ec849c..ed47cf80b 100644 --- a/netbox/netbox/search/__init__.py +++ b/netbox/netbox/search/__init__.py @@ -8,12 +8,18 @@ ObjectFieldValue = namedtuple('ObjectFieldValue', ('name', 'type', 'weight', 'va class FieldTypes: - BOOLEAN = 'bool' FLOAT = 'float' INTEGER = 'int' STRING = 'str' +class LookupTypes: + EXACT = 'iexact' + PARTIAL = 'icontains' + STARTSWITH = 'istartswith' + ENDSWITH = 'iendswith' + + class SearchIndex: """ Base class for building search indexes. @@ -36,8 +42,6 @@ class SearchIndex: @staticmethod def get_field_type(instance, field_name): field_cls = instance._meta.get_field(field_name).__class__ - if issubclass(field_cls, models.BooleanField): - return FieldTypes.BOOLEAN if issubclass(field_cls, (models.FloatField, models.DecimalField)): return FieldTypes.FLOAT if issubclass(field_cls, models.IntegerField): diff --git a/netbox/netbox/search/backends.py b/netbox/netbox/search/backends.py index ee837adbd..f50623cb5 100644 --- a/netbox/netbox/search/backends.py +++ b/netbox/netbox/search/backends.py @@ -11,11 +11,13 @@ from django.db.models.signals import post_delete, post_save from extras.models import CachedValue from extras.registry import registry from netbox.constants import SEARCH_MAX_RESULTS -from . import SearchResult +from . import FieldTypes, LookupTypes, SearchResult # The cache for the initialized backend. _backends_cache = {} +DEFAULT_LOOKUP_TYPE = LookupTypes.PARTIAL + def get_indexer(model): app_label = model._meta.app_label @@ -67,7 +69,7 @@ class SearchBackend: return self._search_choice_options - def search(self, request, value, **kwargs): + def search(self, request, value, lookup=DEFAULT_LOOKUP_TYPE): """ Search cached object representations for the given value. """ @@ -113,7 +115,7 @@ class FilterSetSearchBackend(SearchBackend): Legacy search backend. Performs a discrete database query for each registered object type, using the FilterSet class specified by the index for each. """ - def search(self, request, value, **kwargs): + def search(self, request, value, lookup=DEFAULT_LOOKUP_TYPE): results = [] search_registry = self.get_registry() @@ -153,12 +155,15 @@ class FilterSetSearchBackend(SearchBackend): class CachedValueSearchBackend(SearchBackend): - def search(self, request, value, **kwargs): + def search(self, request, value, lookup=DEFAULT_LOOKUP_TYPE): # Define the search parameters params = { - 'value__icontains': value + f'value__{lookup}': value } + if lookup != LookupTypes.EXACT: + # Partial matches are valid only on string values + params['type'] = FieldTypes.STRING # Construct the base queryset to retrieve matching results queryset = CachedValue.objects.filter(**params).annotate(