diff --git a/netbox/netbox/forms/__init__.py b/netbox/netbox/forms/__init__.py index b2ee6f706..7dadfa8ef 100644 --- a/netbox/netbox/forms/__init__.py +++ b/netbox/netbox/forms/__init__.py @@ -1,7 +1,7 @@ from django import forms from netbox.search.backends import search_backend -from utilities.forms import BootstrapMixin +from utilities.forms import BootstrapMixin, StaticSelectMultiple from .base import * @@ -21,18 +21,21 @@ def build_options(choices): class SearchForm(BootstrapMixin, forms.Form): q = forms.CharField(label='Search') + obj_types = forms.MultipleChoiceField( + choices=[], + required=False, + label='Object type(s)', + widget=StaticSelectMultiple() + ) options = None def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields["obj_type"] = forms.ChoiceField( - choices=search_backend.get_search_choices(), - required=False, - label='Type' - ) + + self.fields['obj_types'].choices = search_backend.get_object_types() def get_options(self): if not self.options: - self.options = build_options(search_backend.get_search_choices()) + self.options = build_options(search_backend.get_object_types()) return self.options diff --git a/netbox/netbox/search/backends.py b/netbox/netbox/search/backends.py index 2d5e5b007..2a6e002e4 100644 --- a/netbox/netbox/search/backends.py +++ b/netbox/netbox/search/backends.py @@ -12,6 +12,7 @@ from extras.models import CachedValue from extras.registry import registry from netbox.constants import SEARCH_MAX_RESULTS from utilities.querysets import RestrictedPrefetch +from utilities.templatetags.builtins.filters import bettertitle from . import FieldTypes, LookupTypes, SearchResult, get_registry # The cache for the initialized backend. @@ -42,7 +43,7 @@ class SearchBackend: post_save.connect(self.caching_handler) post_delete.connect(self.removal_handler) - def get_search_choices(self): + def get_object_types(self): """Return the set of choices for individual object types, organized by category.""" if not self._search_choice_options: @@ -50,7 +51,7 @@ class SearchBackend: categories = defaultdict(dict) for app_label, models in registry['search'].items(): for name, cls in models.items(): - title = cls.model._meta.verbose_name.title() + title = bettertitle(cls.model._meta.verbose_name) value = f'{app_label}.{name}' categories[cls.get_category()][value] = title diff --git a/netbox/netbox/views/__init__.py b/netbox/netbox/views/__init__.py index eb91e0d60..c3f4af833 100644 --- a/netbox/netbox/views/__init__.py +++ b/netbox/netbox/views/__init__.py @@ -150,18 +150,18 @@ class HomeView(View): class SearchView(View): def get(self, request): - form = SearchForm(request.GET) - object_types = None results = [] + # Initialize search form + form = SearchForm(request.GET) if 'q' in request.GET else SearchForm() + if form.is_valid(): # Restrict results by object type - if form.cleaned_data['obj_type']: - app_label, model_name = form.cleaned_data['obj_type'].split('.') - object_types = [ - ContentType.objects.get_by_natural_key(app_label, model_name) - ] + object_types = [] + for obj_type in form.cleaned_data['obj_types']: + app_label, model_name = obj_type.split('.') + object_types.append(ContentType.objects.get_by_natural_key(app_label, model_name)) results = search_backend.search(request, form.cleaned_data['q'], object_types=object_types) diff --git a/netbox/templates/search.html b/netbox/templates/search.html index e3eeb69d5..4942908da 100644 --- a/netbox/templates/search.html +++ b/netbox/templates/search.html @@ -16,10 +16,21 @@ {% endblock tabs %} {% block content-wrapper %} -