mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-24 16:26:09 -06:00
Highlight matched portion of field value
This commit is contained in:
parent
64cb2cc085
commit
6eb2983ccd
@ -269,6 +269,7 @@ class SiteIndex(SearchIndex):
|
||||
('description', 500),
|
||||
('physical_address', 2000),
|
||||
('shipping_address', 2000),
|
||||
('comments', 5000),
|
||||
)
|
||||
|
||||
|
||||
|
@ -4,6 +4,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import FieldDoesNotExist
|
||||
from django.db.models.fields.related import RelatedField
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext as _
|
||||
from django_tables2.data import TableQuerysetData
|
||||
|
||||
@ -11,6 +12,8 @@ from extras.models import CustomField, CustomLink
|
||||
from extras.choices import CustomFieldVisibilityChoices
|
||||
from netbox.tables import columns
|
||||
from utilities.paginator import EnhancedPaginator, get_paginate_count
|
||||
from utilities.templatetags.builtins.filters import bettertitle
|
||||
from utilities.utils import highlight_string
|
||||
|
||||
__all__ = (
|
||||
'BaseTable',
|
||||
@ -206,8 +209,25 @@ class SearchTable(tables.Table):
|
||||
field = tables.Column()
|
||||
value = tables.Column()
|
||||
|
||||
trim_length = 30
|
||||
|
||||
class Meta:
|
||||
attrs = {
|
||||
'class': 'table table-hover object-list',
|
||||
}
|
||||
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)
|
||||
|
@ -23,6 +23,7 @@ from extras.models import ObjectChange
|
||||
from extras.tables import ObjectChangeTable
|
||||
from ipam.models import Aggregate, IPAddress, IPRange, Prefix, VLAN, VRF
|
||||
from netbox.forms import SearchForm
|
||||
from netbox.search import LookupTypes
|
||||
from netbox.search.backends import search_backend
|
||||
from netbox.tables import SearchTable
|
||||
from tenancy.models import Tenant
|
||||
@ -153,6 +154,7 @@ class SearchView(View):
|
||||
|
||||
def get(self, request):
|
||||
results = []
|
||||
highlight = None
|
||||
|
||||
# Initialize search form
|
||||
form = SearchForm(request.GET) if 'q' in request.GET else SearchForm()
|
||||
@ -172,7 +174,10 @@ class SearchView(View):
|
||||
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
|
||||
RequestConfig(request, {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import datetime
|
||||
import decimal
|
||||
import json
|
||||
import re
|
||||
from decimal import Decimal
|
||||
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.functions import Coalesce
|
||||
from django.http import QueryDict
|
||||
from django.utils.html import escape
|
||||
from jinja2.sandbox import SandboxedEnvironment
|
||||
from mptt.models import MPTTModel
|
||||
|
||||
@ -472,3 +474,23 @@ def clean_html(html, schemes):
|
||||
attributes=ALLOWED_ATTRIBUTES,
|
||||
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)}'
|
||||
|
Loading…
Reference in New Issue
Block a user