This commit is contained in:
Jeremy Stretch 2023-10-28 13:34:41 -04:00
parent 77208bf5f3
commit fa25c2c983
6 changed files with 55 additions and 2 deletions

View File

@ -44,6 +44,7 @@ class DeviceIndex(SearchIndex):
('description', 500),
('comments', 5000),
)
display_attrs = ('site', 'location', 'rack', 'device_type', 'description')
@register_search
@ -282,6 +283,7 @@ class SiteIndex(SearchIndex):
('shipping_address', 2000),
('comments', 5000),
)
display_attrs = ('region', 'group', 'status', 'description')
@register_search

View File

@ -4,7 +4,9 @@ from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.utils.translation import gettext_lazy as _
from netbox.registry import registry
from utilities.fields import RestrictedGenericForeignKey
from utilities.utils import content_type_identifier
from ..fields import CachedValueField
__all__ = (
@ -56,3 +58,16 @@ class CachedValue(models.Model):
def __str__(self):
return f'{self.object_type} {self.object_id}: {self.field}={self.value}'
@property
def display_attrs(self):
indexer = registry['search'].get(content_type_identifier(self.object_type))
attrs = {}
for attr in getattr(indexer, 'display_attrs', []):
name = self.object._meta.get_field(attr).verbose_name
if value := getattr(self.object, attr):
if display_func := getattr(self.object, f'get_{attr}_display', None):
attrs[name] = display_func()
else:
attrs[name] = value
return attrs

View File

@ -33,10 +33,12 @@ class SearchIndex:
category: The label of the group under which this indexer is categorized (for form field display). If none,
the name of the model's app will be used.
fields: An iterable of two-tuples defining the model fields to be indexed and the weight associated with each.
display_attrs: An iterable of additional object attributes to include when displaying search results.
"""
model = None
category = None
fields = ()
display_attrs = ()
@staticmethod
def get_field_type(instance, field_name):

View File

@ -3,7 +3,8 @@ from collections import defaultdict
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ImproperlyConfigured
from django.db.models import F, Window, Q
from django.db.models import F, Window, Q, prefetch_related_objects
from django.db.models.fields.related import ForeignKey
from django.db.models.functions import window
from django.db.models.signals import post_delete, post_save
from django.utils.module_loading import import_string
@ -13,7 +14,7 @@ from netaddr.core import AddrFormatError
from extras.models import CachedValue, CustomField
from netbox.registry import registry
from utilities.querysets import RestrictedPrefetch
from utilities.utils import title
from utilities.utils import content_type_identifier, title
from . import FieldTypes, LookupTypes, get_indexer
DEFAULT_LOOKUP_TYPE = LookupTypes.PARTIAL
@ -129,6 +130,10 @@ class CachedValueSearchBackend(SearchBackend):
)
)[:MAX_RESULTS]
# Find the ContentTypes for all objects present in the search results
content_type_ids = set(queryset.values_list('object_type', flat=True))
content_types = ContentType.objects.filter(pk__in=content_type_ids)
# Construct a Prefetch to pre-fetch only those related objects for which the
# user has permission to view.
if user:
@ -144,12 +149,31 @@ class CachedValueSearchBackend(SearchBackend):
params
)
# Prefetch display attributes
for ct in content_types:
model = ct.model_class()
indexer = registry['search'].get(content_type_identifier(ct))
display_attrs = getattr(indexer, 'display_attrs', None)
if not display_attrs:
continue
prefetch_fields = []
for attr in display_attrs:
field = model._meta.get_field(attr)
if type(field) is ForeignKey:
prefetch_fields.append(f'object__{attr}')
if prefetch_fields:
objects = [r for r in results if r.object_type == ct]
prefetch_related_objects(objects, *prefetch_fields)
# Omit any results pertaining to an object the user does not have permission to view
ret = []
for r in results:
if r.object is not None:
r.name = str(r.object)
ret.append(r)
return ret
def cache(self, instances, indexer=None, remove_existing=True):

View File

@ -15,6 +15,7 @@ from extras.choices import CustomFieldVisibilityChoices
from netbox.tables import columns
from utilities.paginator import EnhancedPaginator, get_paginate_count
from utilities.utils import get_viewname, highlight_string, title
from .template_code import *
__all__ = (
'BaseTable',
@ -236,6 +237,10 @@ class SearchTable(tables.Table):
value = tables.Column(
verbose_name=_('Value'),
)
attrs = columns.TemplateColumn(
template_code=SEARCH_RESULT_ATTRS,
verbose_name=_('Attributes')
)
trim_length = 30

View File

@ -0,0 +1,5 @@
SEARCH_RESULT_ATTRS = """
{% for name, value in record.display_attrs.items %}
<span class="badge bg-secondary">{{ name|bettertitle }}: {{ value }}</span>
{% endfor %}
"""