Просмотр исходного кода

Closes #9577: Add has_front_image and has_rear_image filters for device types

jeremystretch 3 лет назад
Родитель
Сommit
ea9d2e3f88

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

@@ -5,6 +5,7 @@
 ### Enhancements
 
 * [#8580](https://github.com/netbox-community/netbox/issues/8580) - Add `occupied` filter for cabled objects to filter by cable or `mark_connected`
+* [#9577](https://github.com/netbox-community/netbox/issues/9577) - Add `has_front_image` and `has_rear_image` filters for device types
 * [#10268](https://github.com/netbox-community/netbox/issues/10268) - Omit trailing ".0" in device positions within UI
 
 ### Bug Fixes

+ 20 - 0
netbox/dcim/filtersets.py

@@ -434,6 +434,14 @@ class DeviceTypeFilterSet(NetBoxModelFilterSet):
         to_field_name='slug',
         label='Manufacturer (slug)',
     )
+    has_front_image = django_filters.BooleanFilter(
+        label='Has a front image',
+        method='_has_front_image'
+    )
+    has_rear_image = django_filters.BooleanFilter(
+        label='Has a rear image',
+        method='_has_rear_image'
+    )
     console_ports = django_filters.BooleanFilter(
         method='_console_ports',
         label='Has console ports',
@@ -487,6 +495,18 @@ class DeviceTypeFilterSet(NetBoxModelFilterSet):
             Q(comments__icontains=value)
         )
 
+    def _has_front_image(self, queryset, name, value):
+        if value:
+            return queryset.exclude(front_image='')
+        else:
+            return queryset.filter(front_image='')
+
+    def _has_rear_image(self, queryset, name, value):
+        if value:
+            return queryset.exclude(rear_image='')
+        else:
+            return queryset.filter(rear_image='')
+
     def _console_ports(self, queryset, name, value):
         return queryset.exclude(consoleporttemplates__isnull=value)
 

+ 15 - 0
netbox/dcim/forms/filtersets.py

@@ -365,6 +365,7 @@ class DeviceTypeFilterForm(NetBoxModelFilterSetForm):
     fieldsets = (
         (None, ('q', 'tag')),
         ('Hardware', ('manufacturer_id', 'part_number', 'subdevice_role', 'airflow')),
+        ('Images', ('has_front_image', 'has_rear_image')),
         ('Components', (
             'console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces',
             'pass_through_ports', 'device_bays', 'module_bays', 'inventory_items',
@@ -386,6 +387,20 @@ class DeviceTypeFilterForm(NetBoxModelFilterSetForm):
         choices=add_blank_choice(DeviceAirflowChoices),
         required=False
     )
+    has_front_image = forms.NullBooleanField(
+        required=False,
+        label='Has a front image',
+        widget=StaticSelect(
+            choices=BOOLEAN_WITH_BLANK_CHOICES
+        )
+    )
+    has_rear_image = forms.NullBooleanField(
+        required=False,
+        label='Has a rear image',
+        widget=StaticSelect(
+            choices=BOOLEAN_WITH_BLANK_CHOICES
+        )
+    )
     console_ports = forms.NullBooleanField(
         required=False,
         label='Has console ports',

+ 15 - 3
netbox/dcim/tests/test_filtersets.py

@@ -688,7 +688,7 @@ class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
         Manufacturer.objects.bulk_create(manufacturers)
 
         device_types = (
-            DeviceType(manufacturer=manufacturers[0], model='Model 1', slug='model-1', part_number='Part Number 1', u_height=1, is_full_depth=True),
+            DeviceType(manufacturer=manufacturers[0], model='Model 1', slug='model-1', part_number='Part Number 1', u_height=1, is_full_depth=True, front_image='front.png', rear_image='rear.png'),
             DeviceType(manufacturer=manufacturers[1], model='Model 2', slug='model-2', part_number='Part Number 2', u_height=2, is_full_depth=True, subdevice_role=SubdeviceRoleChoices.ROLE_PARENT, airflow=DeviceAirflowChoices.AIRFLOW_FRONT_TO_REAR),
             DeviceType(manufacturer=manufacturers[2], model='Model 3', slug='model-3', part_number='Part Number 3', u_height=3, is_full_depth=False, subdevice_role=SubdeviceRoleChoices.ROLE_CHILD, airflow=DeviceAirflowChoices.AIRFLOW_REAR_TO_FRONT),
         )
@@ -753,9 +753,9 @@ class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
     def test_is_full_depth(self):
-        params = {'is_full_depth': 'true'}
+        params = {'is_full_depth': True}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
-        params = {'is_full_depth': 'false'}
+        params = {'is_full_depth': False}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
 
     def test_subdevice_role(self):
@@ -773,6 +773,18 @@ class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
         params = {'manufacturer': [manufacturers[0].slug, manufacturers[1].slug]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
+    def test_has_front_image(self):
+        params = {'has_front_image': True}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'has_front_image': False}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_has_rear_image(self):
+        params = {'has_rear_image': True}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'has_rear_image': False}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
     def test_console_ports(self):
         params = {'console_ports': 'true'}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)