소스 검색

Closes #7087: Add search/filter forms for all organizational models

jeremystretch 4 년 전
부모
커밋
cc0830bf28

+ 1 - 0
docs/release-notes/version-3.0.md

@@ -8,6 +8,7 @@
 * [#6387](https://github.com/netbox-community/netbox/issues/6387) - Add xDSL interface type
 * [#6387](https://github.com/netbox-community/netbox/issues/6387) - Add xDSL interface type
 * [#6988](https://github.com/netbox-community/netbox/issues/6988) - Order tenants alphabetically without regard to group assignment
 * [#6988](https://github.com/netbox-community/netbox/issues/6988) - Order tenants alphabetically without regard to group assignment
 * [#7032](https://github.com/netbox-community/netbox/issues/7032) - Add URM port types
 * [#7032](https://github.com/netbox-community/netbox/issues/7032) - Add URM port types
+* [#7087](https://github.com/netbox-community/netbox/issues/7087) - Add search/filter forms for all organizational models
 * [#7208](https://github.com/netbox-community/netbox/issues/7208) - Add navigation breadcrumbs for custom scripts & reports
 * [#7208](https://github.com/netbox-community/netbox/issues/7208) - Add navigation breadcrumbs for custom scripts & reports
 * [#7239](https://github.com/netbox-community/netbox/issues/7239) - Redirect global search to filtered object list when an object type is selected
 * [#7239](https://github.com/netbox-community/netbox/issues/7239) - Redirect global search to filtered object list when an object type is selected
 
 

+ 12 - 0
netbox/circuits/forms.py

@@ -266,6 +266,18 @@ class CircuitTypeCSVForm(CustomFieldModelCSVForm):
         }
         }
 
 
 
 
+class CircuitTypeFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
+    model = CircuitType
+    field_groups = [
+        ['q'],
+    ]
+    q = forms.CharField(
+        required=False,
+        widget=forms.TextInput(attrs={'placeholder': _('All Fields')}),
+        label=_('Search')
+    )
+
+
 #
 #
 # Circuits
 # Circuits
 #
 #

+ 2 - 0
netbox/circuits/views.py

@@ -144,6 +144,8 @@ class CircuitTypeListView(generic.ObjectListView):
     queryset = CircuitType.objects.annotate(
     queryset = CircuitType.objects.annotate(
         circuit_count=count_related(Circuit, 'type')
         circuit_count=count_related(Circuit, 'type')
     )
     )
+    filterset = filtersets.CircuitTypeFilterSet
+    filterset_form = forms.CircuitTypeFilterForm
     table = tables.CircuitTypeTable
     table = tables.CircuitTypeTable
 
 
 
 

+ 36 - 0
netbox/dcim/forms.py

@@ -696,6 +696,18 @@ class RackRoleBulkEditForm(BootstrapMixin, CustomFieldModelBulkEditForm):
         nullable_fields = ['color', 'description']
         nullable_fields = ['color', 'description']
 
 
 
 
+class RackRoleFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
+    model = RackRole
+    field_groups = [
+        ['q'],
+    ]
+    q = forms.CharField(
+        required=False,
+        widget=forms.TextInput(attrs={'placeholder': _('All Fields')}),
+        label=_('Search')
+    )
+
+
 #
 #
 # Racks
 # Racks
 #
 #
@@ -1240,6 +1252,18 @@ class ManufacturerBulkEditForm(BootstrapMixin, CustomFieldModelBulkEditForm):
         nullable_fields = ['description']
         nullable_fields = ['description']
 
 
 
 
+class ManufacturerFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
+    model = Manufacturer
+    field_groups = [
+        ['q'],
+    ]
+    q = forms.CharField(
+        required=False,
+        widget=forms.TextInput(attrs={'placeholder': _('All Fields')}),
+        label=_('Search')
+    )
+
+
 #
 #
 # Device types
 # Device types
 #
 #
@@ -2076,6 +2100,18 @@ class DeviceRoleBulkEditForm(BootstrapMixin, CustomFieldModelBulkEditForm):
         nullable_fields = ['color', 'description']
         nullable_fields = ['color', 'description']
 
 
 
 
+class DeviceRoleFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
+    model = DeviceRole
+    field_groups = [
+        ['q'],
+    ]
+    q = forms.CharField(
+        required=False,
+        widget=forms.TextInput(attrs={'placeholder': _('All Fields')}),
+        label=_('Search')
+    )
+
+
 #
 #
 # Platforms
 # Platforms
 #
 #

+ 6 - 0
netbox/dcim/views.py

@@ -440,6 +440,8 @@ class RackRoleListView(generic.ObjectListView):
     queryset = RackRole.objects.annotate(
     queryset = RackRole.objects.annotate(
         rack_count=count_related(Rack, 'role')
         rack_count=count_related(Rack, 'role')
     )
     )
+    filterset = filtersets.RackRoleFilterSet
+    filterset_form = forms.RackRoleFilterForm
     table = tables.RackRoleTable
     table = tables.RackRoleTable
 
 
 
 
@@ -684,6 +686,8 @@ class ManufacturerListView(generic.ObjectListView):
         inventoryitem_count=count_related(InventoryItem, 'manufacturer'),
         inventoryitem_count=count_related(InventoryItem, 'manufacturer'),
         platform_count=count_related(Platform, 'manufacturer')
         platform_count=count_related(Platform, 'manufacturer')
     )
     )
+    filterset = filtersets.ManufacturerFilterSet
+    filterset_form = forms.ManufacturerFilterForm
     table = tables.ManufacturerTable
     table = tables.ManufacturerTable
 
 
 
 
@@ -1149,6 +1153,8 @@ class DeviceRoleListView(generic.ObjectListView):
         device_count=count_related(Device, 'device_role'),
         device_count=count_related(Device, 'device_role'),
         vm_count=count_related(VirtualMachine, 'role')
         vm_count=count_related(VirtualMachine, 'role')
     )
     )
+    filterset = filtersets.DeviceRoleFilterSet
+    filterset_form = forms.DeviceRoleFilterForm
     table = tables.DeviceRoleTable
     table = tables.DeviceRoleTable
 
 
 
 

+ 25 - 2
netbox/ipam/forms.py

@@ -256,7 +256,17 @@ class RIRBulkEditForm(BootstrapMixin, CustomFieldModelBulkEditForm):
         nullable_fields = ['is_private', 'description']
         nullable_fields = ['is_private', 'description']
 
 
 
 
-class RIRFilterForm(BootstrapMixin, forms.Form):
+class RIRFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
+    model = RIR
+    field_groups = [
+        ['q'],
+        ['is_private'],
+    ]
+    q = forms.CharField(
+        required=False,
+        widget=forms.TextInput(attrs={'placeholder': _('All Fields')}),
+        label=_('Search')
+    )
     is_private = forms.NullBooleanField(
     is_private = forms.NullBooleanField(
         required=False,
         required=False,
         label=_('Private'),
         label=_('Private'),
@@ -413,6 +423,18 @@ class RoleBulkEditForm(BootstrapMixin, CustomFieldModelBulkEditForm):
         nullable_fields = ['description']
         nullable_fields = ['description']
 
 
 
 
+class RoleFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
+    model = Role
+    field_groups = [
+        ['q'],
+    ]
+    q = forms.CharField(
+        required=False,
+        widget=forms.TextInput(attrs={'placeholder': _('All Fields')}),
+        label=_('Search')
+    )
+
+
 #
 #
 # Prefixes
 # Prefixes
 #
 #
@@ -1460,11 +1482,12 @@ class VLANGroupBulkEditForm(BootstrapMixin, CustomFieldModelBulkEditForm):
         nullable_fields = ['site', 'description']
         nullable_fields = ['site', 'description']
 
 
 
 
-class VLANGroupFilterForm(BootstrapMixin, forms.Form):
+class VLANGroupFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
     field_groups = [
     field_groups = [
         ['q'],
         ['q'],
         ['region', 'sitegroup', 'site', 'location', 'rack']
         ['region', 'sitegroup', 'site', 'location', 'rack']
     ]
     ]
+    model = VLANGroup
     q = forms.CharField(
     q = forms.CharField(
         required=False,
         required=False,
         widget=forms.TextInput(attrs={'placeholder': _('All Fields')}),
         widget=forms.TextInput(attrs={'placeholder': _('All Fields')}),

+ 2 - 0
netbox/ipam/views.py

@@ -283,6 +283,8 @@ class RoleListView(generic.ObjectListView):
         prefix_count=count_related(Prefix, 'role'),
         prefix_count=count_related(Prefix, 'role'),
         vlan_count=count_related(VLAN, 'role')
         vlan_count=count_related(VLAN, 'role')
     )
     )
+    filterset = filtersets.RoleFilterSet
+    filterset_form = forms.RoleFilterForm
     table = tables.RoleTable
     table = tables.RoleTable
 
 
 
 

+ 1 - 1
netbox/project-static/dist/rack_elevation.css

@@ -1 +1 @@
-svg{--nbx-rack-bg: #e9ecef;--nbx-rack-border: #000;--nbx-rack-slot-bg: #e9ecef;--nbx-rack-slot-border: #adb5bd;--nbx-rack-slot-hover-bg: #ced4da;--nbx-rack-link-color: #0d6efd;--nbx-rack-unit-color: #6c757d}svg[data-netbox-color-mode=dark]{--nbx-rack-bg: #343a40;--nbx-rack-border: #6c757d;--nbx-rack-slot-bg: #343a40;--nbx-rack-slot-border: #495057;--nbx-rack-slot-hover-bg: #212529;--nbx-rack-link-color: #9ec5fe;--nbx-rack-unit-color: #6c757d}*{font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:.875rem}rect{box-sizing:border-box}text{text-anchor:middle;dominant-baseline:middle}svg .unit{margin:0;padding:5px 0;fill:var(--nbx-rack-unit-color)}svg .hidden{visibility:hidden}svg .rack{fill:none;stroke-width:2px;stroke:var(--nbx-rack-border);background-color:var(--nbx-rack-bg)}svg .slot{fill:var(--nbx-rack-slot-bg);stroke:var(--nbx-rack-slot-border)}svg .slot:hover{fill:var(--nbx-rack-slot-hover-bg)}svg .slot+.add-device{fill:var(--nbx-rack-link-color);opacity:0;pointer-events:none}svg .slot:hover+.add-device{opacity:1}svg .slot.reserved:hover[class]+.add-device{fill:#000}svg .slot.reserved[class],svg .slot.reserved:hover[class]{fill:url(#reserved)}svg .slot.occupied[class],svg .slot.occupied:hover[class]{fill:url(#occupied)}svg .slot.blocked[class],svg .slot.blocked:hover[class]{fill:url(#blocked)}svg .slot.blocked:hover+.add-device{opacity:0}
+svg{--nbx-rack-bg: #e9ecef;--nbx-rack-border: #000;--nbx-rack-slot-bg: #e9ecef;--nbx-rack-slot-border: #adb5bd;--nbx-rack-slot-hover-bg: #ced4da;--nbx-rack-link-color: #0d6efd;--nbx-rack-unit-color: #6c757d}svg[data-netbox-color-mode=dark]{--nbx-rack-bg: #343a40;--nbx-rack-border: #6c757d;--nbx-rack-slot-bg: #343a40;--nbx-rack-slot-border: #495057;--nbx-rack-slot-hover-bg: #212529;--nbx-rack-link-color: #9ec5fe;--nbx-rack-unit-color: #6c757d}*{font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:.875rem}rect{box-sizing:border-box}text{text-anchor:middle;dominant-baseline:middle}svg .unit{margin:0;padding:5px 0;fill:var(--nbx-rack-unit-color)}svg a{overflow:hidden}svg .hidden{visibility:hidden}svg .rack{fill:none;stroke-width:2px;stroke:var(--nbx-rack-border);background-color:var(--nbx-rack-bg)}svg .slot{fill:var(--nbx-rack-slot-bg);stroke:var(--nbx-rack-slot-border)}svg .slot:hover{fill:var(--nbx-rack-slot-hover-bg)}svg .slot+.add-device{fill:var(--nbx-rack-link-color);opacity:0;pointer-events:none}svg .slot:hover+.add-device{opacity:1}svg .slot.reserved:hover[class]+.add-device{fill:#000}svg .slot.reserved[class],svg .slot.reserved:hover[class]{fill:url(#reserved)}svg .slot.occupied[class],svg .slot.occupied:hover[class]{fill:url(#occupied)}svg .slot.blocked[class],svg .slot.blocked:hover[class]{fill:url(#blocked)}svg .slot.blocked:hover+.add-device{opacity:0}

+ 24 - 0
netbox/virtualization/forms.py

@@ -61,6 +61,18 @@ class ClusterTypeBulkEditForm(BootstrapMixin, CustomFieldModelBulkEditForm):
         nullable_fields = ['description']
         nullable_fields = ['description']
 
 
 
 
+class ClusterTypeFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
+    model = ClusterType
+    field_groups = [
+        ['q'],
+    ]
+    q = forms.CharField(
+        required=False,
+        widget=forms.TextInput(attrs={'placeholder': _('All Fields')}),
+        label=_('Search')
+    )
+
+
 #
 #
 # Cluster groups
 # Cluster groups
 #
 #
@@ -97,6 +109,18 @@ class ClusterGroupBulkEditForm(BootstrapMixin, CustomFieldModelBulkEditForm):
         nullable_fields = ['description']
         nullable_fields = ['description']
 
 
 
 
+class ClusterGroupFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
+    model = ClusterGroup
+    field_groups = [
+        ['q'],
+    ]
+    q = forms.CharField(
+        required=False,
+        widget=forms.TextInput(attrs={'placeholder': _('All Fields')}),
+        label=_('Search')
+    )
+
+
 #
 #
 # Clusters
 # Clusters
 #
 #

+ 4 - 0
netbox/virtualization/views.py

@@ -24,6 +24,8 @@ class ClusterTypeListView(generic.ObjectListView):
     queryset = ClusterType.objects.annotate(
     queryset = ClusterType.objects.annotate(
         cluster_count=count_related(Cluster, 'type')
         cluster_count=count_related(Cluster, 'type')
     )
     )
+    filterset = filtersets.ClusterTypeFilterSet
+    filterset_form = forms.ClusterTypeFilterForm
     table = tables.ClusterTypeTable
     table = tables.ClusterTypeTable
 
 
 
 
@@ -86,6 +88,8 @@ class ClusterGroupListView(generic.ObjectListView):
     queryset = ClusterGroup.objects.annotate(
     queryset = ClusterGroup.objects.annotate(
         cluster_count=count_related(Cluster, 'group')
         cluster_count=count_related(Cluster, 'group')
     )
     )
+    filterset = filtersets.ClusterGroupFilterSet
+    filterset_form = forms.ClusterGroupFilterForm
     table = tables.ClusterGroupTable
     table = tables.ClusterGroupTable