Highlight matched portion of field value

This commit is contained in:
jeremystretch 2022-10-19 22:20:53 -04:00
parent 64cb2cc085
commit 6eb2983ccd
4 changed files with 49 additions and 1 deletions

View File

@ -269,6 +269,7 @@ class SiteIndex(SearchIndex):
('description', 500), ('description', 500),
('physical_address', 2000), ('physical_address', 2000),
('shipping_address', 2000), ('shipping_address', 2000),
('comments', 5000),
) )

View File

@ -4,6 +4,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import FieldDoesNotExist from django.core.exceptions import FieldDoesNotExist
from django.db.models.fields.related import RelatedField from django.db.models.fields.related import RelatedField
from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django_tables2.data import TableQuerysetData from django_tables2.data import TableQuerysetData
@ -11,6 +12,8 @@ from extras.models import CustomField, CustomLink
from extras.choices import CustomFieldVisibilityChoices from extras.choices import CustomFieldVisibilityChoices
from netbox.tables import columns from netbox.tables import columns
from utilities.paginator import EnhancedPaginator, get_paginate_count from utilities.paginator import EnhancedPaginator, get_paginate_count
from utilities.templatetags.builtins.filters import bettertitle
from utilities.utils import highlight_string
__all__ = ( __all__ = (
'BaseTable', 'BaseTable',
@ -206,8 +209,25 @@ class SearchTable(tables.Table):
field = tables.Column() field = tables.Column()
value = tables.Column() value = tables.Column()
trim_length = 30
class Meta: class Meta:
attrs = { attrs = {
'class': 'table table-hover object-list', 'class': 'table table-hover object-list',
} }
empty_text = _('No results found') empty_text = _('No results found')
def __init__(self, data, highlight=None, **kwargs):
self.highlight = highlight
super().__init__(data, **kwargs)
def render_field(self, value, record):
return bettertitle(record.object._meta.get_field(value).verbose_name)
def render_value(self, value):
if not self.highlight:
return value
value = highlight_string(value, self.highlight, trim_pre=self.trim_length, trim_post=self.trim_length)
return mark_safe(value)

View File

@ -23,6 +23,7 @@ from extras.models import ObjectChange
from extras.tables import ObjectChangeTable from extras.tables import ObjectChangeTable
from ipam.models import Aggregate, IPAddress, IPRange, Prefix, VLAN, VRF from ipam.models import Aggregate, IPAddress, IPRange, Prefix, VLAN, VRF
from netbox.forms import SearchForm from netbox.forms import SearchForm
from netbox.search import LookupTypes
from netbox.search.backends import search_backend from netbox.search.backends import search_backend
from netbox.tables import SearchTable from netbox.tables import SearchTable
from tenancy.models import Tenant from tenancy.models import Tenant
@ -153,6 +154,7 @@ class SearchView(View):
def get(self, request): def get(self, request):
results = [] results = []
highlight = None
# Initialize search form # Initialize search form
form = SearchForm(request.GET) if 'q' in request.GET else SearchForm() form = SearchForm(request.GET) if 'q' in request.GET else SearchForm()
@ -172,7 +174,10 @@ class SearchView(View):
lookup=form.cleaned_data['lookup'] lookup=form.cleaned_data['lookup']
) )
table = SearchTable(results) if form.cleaned_data['lookup'] != LookupTypes.EXACT:
highlight = form.cleaned_data['q']
table = SearchTable(results, highlight=highlight)
# Paginate the table results # Paginate the table results
RequestConfig(request, { RequestConfig(request, {

View File

@ -1,6 +1,7 @@
import datetime import datetime
import decimal import decimal
import json import json
import re
from decimal import Decimal from decimal import Decimal
from itertools import count, groupby from itertools import count, groupby
@ -9,6 +10,7 @@ from django.core.serializers import serialize
from django.db.models import Count, OuterRef, Subquery from django.db.models import Count, OuterRef, Subquery
from django.db.models.functions import Coalesce from django.db.models.functions import Coalesce
from django.http import QueryDict from django.http import QueryDict
from django.utils.html import escape
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from mptt.models import MPTTModel from mptt.models import MPTTModel
@ -472,3 +474,23 @@ def clean_html(html, schemes):
attributes=ALLOWED_ATTRIBUTES, attributes=ALLOWED_ATTRIBUTES,
protocols=schemes protocols=schemes
) )
def highlight_string(value, highlight, trim_pre=None, trim_post=None, trim_placeholder='...'):
"""
Highlight a string within a string and optionally trim the pre/post portions of the original string.
"""
# Split value on highlight string
try:
pre, match, post = re.split(fr'({highlight})', value, maxsplit=1, flags=re.IGNORECASE)
except ValueError:
# Match not found
return escape(value)
# Trim pre/post sections to length
if trim_pre and len(pre) > trim_pre:
pre = trim_placeholder + pre[-trim_pre:]
if trim_post and len(post) > trim_post:
post = post[:trim_post] + trim_placeholder
return f'{escape(pre)}<mark>{escape(match)}</mark>{escape(post)}'