Fixes #7604: Add filter modifier dropdowns for advanced lookup operators

Implements dynamic filter modifier UI that allows users to select lookup operators
(exact, contains, starts with, regex, negation, empty/not empty) directly in filter
forms without manual URL parameter editing.

Supports filters for all scalar types and strings, as well as some
related object filters. Explicitly does not support filters on fields
that use APIWidget. That has been broken out in to follow up work.

**Backend:**
- FilterModifierWidget: Wraps form widgets with lookup modifier dropdown
- FilterModifierMixin: Auto-enhances filterset fields with appropriate lookups
- Extended lookup support: Adds negation (n), regex, iregex, empty_true/false lookups
- Field-type-aware: CharField gets text lookups, IntegerField gets comparison operators, etc.

**Frontend:**
- TypeScript handler syncs modifier dropdown with URL parameters
- Dynamically updates form field names (serial → serial__ic) on modifier change
- Flexible-width modifier dropdowns with semantic CSS classes
This commit is contained in:
Jason Novinger
2025-10-26 22:49:59 -05:00
parent 20c260b126
commit 8445a63786
17 changed files with 98785 additions and 1353 deletions

View File

@@ -13,8 +13,11 @@ from netbox.forms import NetBoxModelFilterSetForm, OrganizationalModelFilterSetF
from tenancy.forms import TenancyFilterForm, ContactModelFilterForm
from utilities.forms import add_blank_choice
from utilities.forms.fields import ColorField, DynamicModelMultipleChoiceField, TagFilterField
from utilities.forms.filterset_mappings import FILTERSET_MAPPINGS
from utilities.forms.mixins import FilterModifierMixin
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import DatePicker, NumberWithOptions
from circuits.filtersets import CircuitFilterSet
__all__ = (
'CircuitFilterForm',
@@ -118,7 +121,7 @@ class CircuitTypeFilterForm(OrganizationalModelFilterSetForm):
)
class CircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, PrimaryModelFilterSetForm):
class CircuitFilterForm(FilterModifierMixin, TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
model = Circuit
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
@@ -397,3 +400,7 @@ class VirtualCircuitTerminationFilterForm(NetBoxModelFilterSetForm):
label=_('Provider')
)
tag = TagFilterField(model)
# Register FilterSet mappings for FilterModifierMixin lookup verification
FILTERSET_MAPPINGS[CircuitFilterForm] = CircuitFilterSet