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

Adds device type and role to device component filter (#12504)

* adds device type and role to device component filter #12015

* changes as per review

* Add filterset tests for device type & role filters

---------

Co-authored-by: jeremystretch <jstretch@netboxlabs.com>
Abhimanyu Saharan 2 лет назад
Родитель
Сommit
bd88ee7063
3 измененных файлов с 278 добавлено и 78 удалено
  1. 22 0
      netbox/dcim/filtersets.py
  2. 33 12
      netbox/dcim/forms/filtersets.py
  3. 223 66
      netbox/dcim/tests/test_filtersets.py

+ 22 - 0
netbox/dcim/filtersets.py

@@ -1219,6 +1219,28 @@ class DeviceComponentFilterSet(django_filters.FilterSet):
         to_field_name='name',
         label=_('Device (name)'),
     )
+    device_type_id = django_filters.ModelMultipleChoiceFilter(
+        field_name='device__device_type',
+        queryset=DeviceType.objects.all(),
+        label=_('Device type (ID)'),
+    )
+    device_type = django_filters.ModelMultipleChoiceFilter(
+        field_name='device__device_type__model',
+        queryset=DeviceType.objects.all(),
+        to_field_name='model',
+        label=_('Device type (model)'),
+    )
+    device_role_id = django_filters.ModelMultipleChoiceFilter(
+        field_name='device__device_role',
+        queryset=DeviceRole.objects.all(),
+        label=_('Device role (ID)'),
+    )
+    device_role = django_filters.ModelMultipleChoiceFilter(
+        field_name='device__device_role__slug',
+        queryset=DeviceRole.objects.all(),
+        to_field_name='slug',
+        label=_('Device role (slug)'),
+    )
     virtual_chassis_id = django_filters.ModelMultipleChoiceFilter(
         field_name='device__virtual_chassis',
         queryset=VirtualChassis.objects.all(),

+ 33 - 12
netbox/dcim/forms/filtersets.py

@@ -102,13 +102,25 @@ class DeviceComponentFilterForm(NetBoxModelFilterSetForm):
         required=False,
         label=_('Virtual Chassis')
     )
+    device_type_id = DynamicModelMultipleChoiceField(
+        queryset=DeviceType.objects.all(),
+        required=False,
+        label=_('Device type')
+    )
+    device_role_id = DynamicModelMultipleChoiceField(
+        queryset=DeviceRole.objects.all(),
+        required=False,
+        label=_('Device role')
+    )
     device_id = DynamicModelMultipleChoiceField(
         queryset=Device.objects.all(),
         required=False,
         query_params={
             'site_id': '$site_id',
             'location_id': '$location_id',
-            'virtual_chassis_id': '$virtual_chassis_id'
+            'virtual_chassis_id': '$virtual_chassis_id',
+            'device_type_id': '$device_type_id',
+            'role_id': '$device_role_id'
         },
         label=_('Device')
     )
@@ -1070,7 +1082,8 @@ class ConsolePortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
     fieldsets = (
         (None, ('q', 'filter_id', 'tag')),
         ('Attributes', ('name', 'label', 'type', 'speed')),
-        ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')),
+        ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id')),
+        ('Device', ('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id')),
         ('Connection', ('cabled', 'connected', 'occupied')),
     )
     type = forms.MultipleChoiceField(
@@ -1089,7 +1102,8 @@ class ConsoleServerPortFilterForm(PathEndpointFilterForm, DeviceComponentFilterF
     fieldsets = (
         (None, ('q', 'filter_id', 'tag')),
         ('Attributes', ('name', 'label', 'type', 'speed')),
-        ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')),
+        ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id')),
+        ('Device', ('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id')),
         ('Connection', ('cabled', 'connected', 'occupied')),
     )
     type = forms.MultipleChoiceField(
@@ -1108,7 +1122,8 @@ class PowerPortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
     fieldsets = (
         (None, ('q', 'filter_id', 'tag')),
         ('Attributes', ('name', 'label', 'type')),
-        ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')),
+        ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id')),
+        ('Device', ('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id')),
         ('Connection', ('cabled', 'connected', 'occupied')),
     )
     type = forms.MultipleChoiceField(
@@ -1123,7 +1138,8 @@ class PowerOutletFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
     fieldsets = (
         (None, ('q', 'filter_id', 'tag')),
         ('Attributes', ('name', 'label', 'type')),
-        ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')),
+        ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id')),
+        ('Device', ('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id')),
         ('Connection', ('cabled', 'connected', 'occupied')),
     )
     type = forms.MultipleChoiceField(
@@ -1141,8 +1157,8 @@ class InterfaceFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
         ('Addressing', ('vrf_id', 'l2vpn_id', 'mac_address', 'wwn')),
         ('PoE', ('poe_mode', 'poe_type')),
         ('Wireless', ('rf_role', 'rf_channel', 'rf_channel_width', 'tx_power')),
-        ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id',
-                    'device_id', 'vdc_id')),
+        ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id')),
+        ('Device', ('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id', 'vdc_id')),
         ('Connection', ('cabled', 'connected', 'occupied')),
     )
     vdc_id = DynamicModelMultipleChoiceField(
@@ -1242,7 +1258,8 @@ class FrontPortFilterForm(CabledFilterForm, DeviceComponentFilterForm):
     fieldsets = (
         (None, ('q', 'filter_id', 'tag')),
         ('Attributes', ('name', 'label', 'type', 'color')),
-        ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')),
+        ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id')),
+        ('Device', ('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id')),
         ('Cable', ('cabled', 'occupied')),
     )
     model = FrontPort
@@ -1261,7 +1278,8 @@ class RearPortFilterForm(CabledFilterForm, DeviceComponentFilterForm):
     fieldsets = (
         (None, ('q', 'filter_id', 'tag')),
         ('Attributes', ('name', 'label', 'type', 'color')),
-        ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')),
+        ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id')),
+        ('Device', ('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id')),
         ('Cable', ('cabled', 'occupied')),
     )
     type = forms.MultipleChoiceField(
@@ -1279,7 +1297,8 @@ class ModuleBayFilterForm(DeviceComponentFilterForm):
     fieldsets = (
         (None, ('q', 'filter_id', 'tag')),
         ('Attributes', ('name', 'label', 'position')),
-        ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')),
+        ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id')),
+        ('Device', ('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id')),
     )
     tag = TagFilterField(model)
     position = forms.CharField(
@@ -1292,7 +1311,8 @@ class DeviceBayFilterForm(DeviceComponentFilterForm):
     fieldsets = (
         (None, ('q', 'filter_id', 'tag')),
         ('Attributes', ('name', 'label')),
-        ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')),
+        ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id')),
+        ('Device', ('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id')),
     )
     tag = TagFilterField(model)
 
@@ -1302,7 +1322,8 @@ class InventoryItemFilterForm(DeviceComponentFilterForm):
     fieldsets = (
         (None, ('q', 'filter_id', 'tag')),
         ('Attributes', ('name', 'label', 'role_id', 'manufacturer_id', 'serial', 'asset_tag', 'discovered')),
-        ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')),
+        ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id')),
+        ('Device', ('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id')),
     )
     role_id = DynamicModelMultipleChoiceField(
         queryset=InventoryItemRole.objects.all(),

+ 223 - 66
netbox/dcim/tests/test_filtersets.py

@@ -12,6 +12,23 @@ from virtualization.models import Cluster, ClusterType
 from wireless.choices import WirelessChannelChoices, WirelessRoleChoices
 
 
+class DeviceComponentFilterSetTests:
+
+    def test_device_type(self):
+        device_types = DeviceType.objects.all()[:2]
+        params = {'device_type_id': [device_types[0].pk, device_types[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'device_type': [device_types[0].model, device_types[1].model]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_device_role(self):
+        device_role = DeviceRole.objects.all()[:2]
+        params = {'device_role_id': [device_role[0].pk, device_role[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'device_role': [device_role[0].slug, device_role[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
 class RegionTestCase(TestCase, ChangeLoggedFilterSetTests):
     queryset = Region.objects.all()
     filterset = RegionFilterSet
@@ -1998,7 +2015,7 @@ class ModuleTestCase(TestCase, ChangeLoggedFilterSetTests):
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
 
-class ConsolePortTestCase(TestCase, ChangeLoggedFilterSetTests):
+class ConsolePortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilterSetTests):
     queryset = ConsolePort.objects.all()
     filterset = ConsolePortFilterSet
 
@@ -2027,10 +2044,23 @@ class ConsolePortTestCase(TestCase, ChangeLoggedFilterSetTests):
             Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
             Site(name='Site X', slug='site-x'),
         ))
+
         manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
-        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 3', slug='device-type-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
         module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Module Type 1')
-        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        device_roles = (
+            DeviceRole(name='Device Role 1', slug='device-role-1'),
+            DeviceRole(name='Device Role 2', slug='device-role-2'),
+            DeviceRole(name='Device Role 3', slug='device-role-3'),
+        )
+        DeviceRole.objects.bulk_create(device_roles)
 
         locations = (
             Location(name='Location 1', slug='location-1', site=sites[0]),
@@ -2048,10 +2078,10 @@ class ConsolePortTestCase(TestCase, ChangeLoggedFilterSetTests):
         Rack.objects.bulk_create(racks)
 
         devices = (
-            Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], location=locations[0], rack=racks[0]),
-            Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[1], location=locations[1], rack=racks[1]),
-            Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[2], location=locations[2], rack=racks[2]),
-            Device(name=None, device_type=device_type, device_role=device_role, site=sites[3]),  # For cable connections
+            Device(name='Device 1', device_type=device_types[0], device_role=device_roles[0], site=sites[0], location=locations[0], rack=racks[0]),
+            Device(name='Device 2', device_type=device_types[1], device_role=device_roles[1], site=sites[1], location=locations[1], rack=racks[1]),
+            Device(name='Device 3', device_type=device_types[2], device_role=device_roles[2], site=sites[2], location=locations[2], rack=racks[2]),
+            Device(name=None, device_type=device_types[0], device_role=device_roles[0], site=sites[3]),  # For cable connections
         )
         Device.objects.bulk_create(devices)
 
@@ -2165,7 +2195,7 @@ class ConsolePortTestCase(TestCase, ChangeLoggedFilterSetTests):
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
 
 
-class ConsoleServerPortTestCase(TestCase, ChangeLoggedFilterSetTests):
+class ConsoleServerPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilterSetTests):
     queryset = ConsoleServerPort.objects.all()
     filterset = ConsoleServerPortFilterSet
 
@@ -2194,10 +2224,23 @@ class ConsoleServerPortTestCase(TestCase, ChangeLoggedFilterSetTests):
             Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
             Site(name='Site X', slug='site-x'),
         ))
+
         manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
-        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 3', slug='device-type-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
         module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Module Type 1')
-        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        device_roles = (
+            DeviceRole(name='Device Role 1', slug='device-role-1'),
+            DeviceRole(name='Device Role 2', slug='device-role-2'),
+            DeviceRole(name='Device Role 3', slug='device-role-3'),
+        )
+        DeviceRole.objects.bulk_create(device_roles)
 
         locations = (
             Location(name='Location 1', slug='location-1', site=sites[0]),
@@ -2215,10 +2258,10 @@ class ConsoleServerPortTestCase(TestCase, ChangeLoggedFilterSetTests):
         Rack.objects.bulk_create(racks)
 
         devices = (
-            Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], location=locations[0], rack=racks[0]),
-            Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[1], location=locations[1], rack=racks[1]),
-            Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[2], location=locations[2], rack=racks[2]),
-            Device(name=None, device_type=device_type, device_role=device_role, site=sites[3]),  # For cable connections
+            Device(name='Device 1', device_type=device_types[0], device_role=device_roles[0], site=sites[0], location=locations[0], rack=racks[0]),
+            Device(name='Device 2', device_type=device_types[1], device_role=device_roles[1], site=sites[1], location=locations[1], rack=racks[1]),
+            Device(name='Device 3', device_type=device_types[2], device_role=device_roles[2], site=sites[2], location=locations[2], rack=racks[2]),
+            Device(name=None, device_type=device_types[2], device_role=device_roles[2], site=sites[3]),  # For cable connections
         )
         Device.objects.bulk_create(devices)
 
@@ -2332,7 +2375,7 @@ class ConsoleServerPortTestCase(TestCase, ChangeLoggedFilterSetTests):
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
 
 
-class PowerPortTestCase(TestCase, ChangeLoggedFilterSetTests):
+class PowerPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilterSetTests):
     queryset = PowerPort.objects.all()
     filterset = PowerPortFilterSet
 
@@ -2361,10 +2404,23 @@ class PowerPortTestCase(TestCase, ChangeLoggedFilterSetTests):
             Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
             Site(name='Site X', slug='site-x'),
         ))
+
         manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
-        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 3', slug='device-type-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
         module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Module Type 1')
-        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        device_roles = (
+            DeviceRole(name='Device Role 1', slug='device-role-1'),
+            DeviceRole(name='Device Role 2', slug='device-role-2'),
+            DeviceRole(name='Device Role 3', slug='device-role-3'),
+        )
+        DeviceRole.objects.bulk_create(device_roles)
 
         locations = (
             Location(name='Location 1', slug='location-1', site=sites[0]),
@@ -2382,10 +2438,10 @@ class PowerPortTestCase(TestCase, ChangeLoggedFilterSetTests):
         Rack.objects.bulk_create(racks)
 
         devices = (
-            Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], location=locations[0], rack=racks[0]),
-            Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[1], location=locations[1], rack=racks[1]),
-            Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[2], location=locations[2], rack=racks[2]),
-            Device(name=None, device_type=device_type, device_role=device_role, site=sites[3]),  # For cable connections
+            Device(name='Device 1', device_type=device_types[0], device_role=device_roles[0], site=sites[0], location=locations[0], rack=racks[0]),
+            Device(name='Device 2', device_type=device_types[1], device_role=device_roles[1], site=sites[1], location=locations[1], rack=racks[1]),
+            Device(name='Device 3', device_type=device_types[2], device_role=device_roles[2], site=sites[2], location=locations[2], rack=racks[2]),
+            Device(name=None, device_type=device_types[2], device_role=device_roles[2], site=sites[3]),  # For cable connections
         )
         Device.objects.bulk_create(devices)
 
@@ -2507,7 +2563,7 @@ class PowerPortTestCase(TestCase, ChangeLoggedFilterSetTests):
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
 
 
-class PowerOutletTestCase(TestCase, ChangeLoggedFilterSetTests):
+class PowerOutletTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilterSetTests):
     queryset = PowerOutlet.objects.all()
     filterset = PowerOutletFilterSet
 
@@ -2536,10 +2592,23 @@ class PowerOutletTestCase(TestCase, ChangeLoggedFilterSetTests):
             Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
             Site(name='Site X', slug='site-x'),
         ))
+
         manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
-        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 3', slug='device-type-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
         module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Module Type 1')
-        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        device_roles = (
+            DeviceRole(name='Device Role 1', slug='device-role-1'),
+            DeviceRole(name='Device Role 2', slug='device-role-2'),
+            DeviceRole(name='Device Role 3', slug='device-role-3'),
+        )
+        DeviceRole.objects.bulk_create(device_roles)
 
         locations = (
             Location(name='Location 1', slug='location-1', site=sites[0]),
@@ -2557,10 +2626,10 @@ class PowerOutletTestCase(TestCase, ChangeLoggedFilterSetTests):
         Rack.objects.bulk_create(racks)
 
         devices = (
-            Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], location=locations[0], rack=racks[0]),
-            Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[1], location=locations[1], rack=racks[1]),
-            Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[2], location=locations[2], rack=racks[2]),
-            Device(name=None, device_type=device_type, device_role=device_role, site=sites[3]),  # For cable connections
+            Device(name='Device 1', device_type=device_types[0], device_role=device_roles[0], site=sites[0], location=locations[0], rack=racks[0]),
+            Device(name='Device 2', device_type=device_types[1], device_role=device_roles[1], site=sites[1], location=locations[1], rack=racks[1]),
+            Device(name='Device 3', device_type=device_types[2], device_role=device_roles[2], site=sites[2], location=locations[2], rack=racks[2]),
+            Device(name=None, device_type=device_types[2], device_role=device_roles[2], site=sites[3]),  # For cable connections
         )
         Device.objects.bulk_create(devices)
 
@@ -2678,7 +2747,7 @@ class PowerOutletTestCase(TestCase, ChangeLoggedFilterSetTests):
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
 
 
-class InterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
+class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilterSetTests):
     queryset = Interface.objects.all()
     filterset = InterfaceFilterSet
 
@@ -2707,10 +2776,23 @@ class InterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
             Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
             Site(name='Site X', slug='site-x'),
         ))
+
         manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
-        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 3', slug='device-type-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
         module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Module Type 1')
-        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        device_roles = (
+            DeviceRole(name='Device Role 1', slug='device-role-1'),
+            DeviceRole(name='Device Role 2', slug='device-role-2'),
+            DeviceRole(name='Device Role 3', slug='device-role-3'),
+        )
+        DeviceRole.objects.bulk_create(device_roles)
 
         locations = (
             Location(name='Location 1', slug='location-1', site=sites[0]),
@@ -2728,10 +2810,10 @@ class InterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
         Rack.objects.bulk_create(racks)
 
         devices = (
-            Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], location=locations[0], rack=racks[0]),
-            Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[1], location=locations[1], rack=racks[1]),
-            Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[2], location=locations[2], rack=racks[2]),
-            Device(name=None, device_type=device_type, device_role=device_role, site=sites[3]),  # For cable connections
+            Device(name='Device 1', device_type=device_types[0], device_role=device_roles[0], site=sites[0], location=locations[0], rack=racks[0]),
+            Device(name='Device 2', device_type=device_types[1], device_role=device_roles[1], site=sites[1], location=locations[1], rack=racks[1]),
+            Device(name='Device 3', device_type=device_types[2], device_role=device_roles[2], site=sites[2], location=locations[2], rack=racks[2]),
+            Device(name=None, device_type=device_types[2], device_role=device_roles[2], site=sites[3]),  # For cable connections
         )
         Device.objects.bulk_create(devices)
 
@@ -3101,7 +3183,7 @@ class InterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
 
 
-class FrontPortTestCase(TestCase, ChangeLoggedFilterSetTests):
+class FrontPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilterSetTests):
     queryset = FrontPort.objects.all()
     filterset = FrontPortFilterSet
 
@@ -3130,10 +3212,23 @@ class FrontPortTestCase(TestCase, ChangeLoggedFilterSetTests):
             Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
             Site(name='Site X', slug='site-x'),
         ))
+
         manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
-        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 3', slug='device-type-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
         module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Module Type 1')
-        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        device_roles = (
+            DeviceRole(name='Device Role 1', slug='device-role-1'),
+            DeviceRole(name='Device Role 2', slug='device-role-2'),
+            DeviceRole(name='Device Role 3', slug='device-role-3'),
+        )
+        DeviceRole.objects.bulk_create(device_roles)
 
         locations = (
             Location(name='Location 1', slug='location-1', site=sites[0]),
@@ -3151,10 +3246,10 @@ class FrontPortTestCase(TestCase, ChangeLoggedFilterSetTests):
         Rack.objects.bulk_create(racks)
 
         devices = (
-            Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], location=locations[0], rack=racks[0]),
-            Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[1], location=locations[1], rack=racks[1]),
-            Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[2], location=locations[2], rack=racks[2]),
-            Device(name=None, device_type=device_type, device_role=device_role, site=sites[3]),  # For cable connections
+            Device(name='Device 1', device_type=device_types[0], device_role=device_roles[0], site=sites[0], location=locations[0], rack=racks[0]),
+            Device(name='Device 2', device_type=device_types[1], device_role=device_roles[1], site=sites[1], location=locations[1], rack=racks[1]),
+            Device(name='Device 3', device_type=device_types[2], device_role=device_roles[2], site=sites[2], location=locations[2], rack=racks[2]),
+            Device(name=None, device_type=device_types[2], device_role=device_roles[2], site=sites[3]),  # For cable connections
         )
         Device.objects.bulk_create(devices)
 
@@ -3277,7 +3372,7 @@ class FrontPortTestCase(TestCase, ChangeLoggedFilterSetTests):
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
 
-class RearPortTestCase(TestCase, ChangeLoggedFilterSetTests):
+class RearPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilterSetTests):
     queryset = RearPort.objects.all()
     filterset = RearPortFilterSet
 
@@ -3306,10 +3401,23 @@ class RearPortTestCase(TestCase, ChangeLoggedFilterSetTests):
             Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
             Site(name='Site X', slug='site-x'),
         ))
+
         manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
-        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 3', slug='device-type-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
         module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Module Type 1')
-        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+
+        device_roles = (
+            DeviceRole(name='Device Role 1', slug='device-role-1'),
+            DeviceRole(name='Device Role 2', slug='device-role-2'),
+            DeviceRole(name='Device Role 3', slug='device-role-3'),
+        )
+        DeviceRole.objects.bulk_create(device_roles)
 
         locations = (
             Location(name='Location 1', slug='location-1', site=sites[0]),
@@ -3327,10 +3435,10 @@ class RearPortTestCase(TestCase, ChangeLoggedFilterSetTests):
         Rack.objects.bulk_create(racks)
 
         devices = (
-            Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], location=locations[0], rack=racks[0]),
-            Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[1], location=locations[1], rack=racks[1]),
-            Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[2], location=locations[2], rack=racks[2]),
-            Device(name=None, device_type=device_type, device_role=device_role, site=sites[3]),  # For cable connections
+            Device(name='Device 1', device_type=device_types[0], device_role=device_roles[0], site=sites[0], location=locations[0], rack=racks[0]),
+            Device(name='Device 2', device_type=device_types[1], device_role=device_roles[1], site=sites[1], location=locations[1], rack=racks[1]),
+            Device(name='Device 3', device_type=device_types[2], device_role=device_roles[2], site=sites[2], location=locations[2], rack=racks[2]),
+            Device(name=None, device_type=device_types[2], device_role=device_roles[2], site=sites[3]),  # For cable connections
         )
         Device.objects.bulk_create(devices)
 
@@ -3447,7 +3555,7 @@ class RearPortTestCase(TestCase, ChangeLoggedFilterSetTests):
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
 
-class ModuleBayTestCase(TestCase, ChangeLoggedFilterSetTests):
+class ModuleBayTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilterSetTests):
     queryset = ModuleBay.objects.all()
     filterset = ModuleBayFilterSet
 
@@ -3476,9 +3584,21 @@ class ModuleBayTestCase(TestCase, ChangeLoggedFilterSetTests):
             Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
             Site(name='Site X', slug='site-x'),
         ))
+
         manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
-        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Model 1', slug='model-1')
-        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 3', slug='device-type-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
+        device_roles = (
+            DeviceRole(name='Device Role 1', slug='device-role-1'),
+            DeviceRole(name='Device Role 2', slug='device-role-2'),
+            DeviceRole(name='Device Role 3', slug='device-role-3'),
+        )
+        DeviceRole.objects.bulk_create(device_roles)
 
         locations = (
             Location(name='Location 1', slug='location-1', site=sites[0]),
@@ -3496,9 +3616,9 @@ class ModuleBayTestCase(TestCase, ChangeLoggedFilterSetTests):
         Rack.objects.bulk_create(racks)
 
         devices = (
-            Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], location=locations[0], rack=racks[0]),
-            Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[1], location=locations[1], rack=racks[1]),
-            Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[2], location=locations[2], rack=racks[2]),
+            Device(name='Device 1', device_type=device_types[0], device_role=device_roles[0], site=sites[0], location=locations[0], rack=racks[0]),
+            Device(name='Device 2', device_type=device_types[1], device_role=device_roles[1], site=sites[1], location=locations[1], rack=racks[1]),
+            Device(name='Device 3', device_type=device_types[2], device_role=device_roles[2], site=sites[2], location=locations[2], rack=racks[2]),
         )
         Device.objects.bulk_create(devices)
 
@@ -3564,7 +3684,7 @@ class ModuleBayTestCase(TestCase, ChangeLoggedFilterSetTests):
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
 
-class DeviceBayTestCase(TestCase, ChangeLoggedFilterSetTests):
+class DeviceBayTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilterSetTests):
     queryset = DeviceBay.objects.all()
     filterset = DeviceBayFilterSet
 
@@ -3593,9 +3713,21 @@ class DeviceBayTestCase(TestCase, ChangeLoggedFilterSetTests):
             Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
             Site(name='Site X', slug='site-x'),
         ))
+
         manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
-        device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Model 1', slug='model-1')
-        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+        device_types = (
+            DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
+            DeviceType(manufacturer=manufacturer, model='Device Type 3', slug='device-type-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
+        device_roles = (
+            DeviceRole(name='Device Role 1', slug='device-role-1'),
+            DeviceRole(name='Device Role 2', slug='device-role-2'),
+            DeviceRole(name='Device Role 3', slug='device-role-3'),
+        )
+        DeviceRole.objects.bulk_create(device_roles)
 
         locations = (
             Location(name='Location 1', slug='location-1', site=sites[0]),
@@ -3613,9 +3745,9 @@ class DeviceBayTestCase(TestCase, ChangeLoggedFilterSetTests):
         Rack.objects.bulk_create(racks)
 
         devices = (
-            Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], location=locations[0], rack=racks[0]),
-            Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[1], location=locations[1], rack=racks[1]),
-            Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[2], location=locations[2], rack=racks[2]),
+            Device(name='Device 1', device_type=device_types[0], device_role=device_roles[0], site=sites[0], location=locations[0], rack=racks[0]),
+            Device(name='Device 2', device_type=device_types[1], device_role=device_roles[1], site=sites[1], location=locations[1], rack=racks[1]),
+            Device(name='Device 3', device_type=device_types[2], device_role=device_roles[2], site=sites[2], location=locations[2], rack=racks[2]),
         )
         Device.objects.bulk_create(devices)
 
@@ -3694,8 +3826,19 @@ class InventoryItemTestCase(TestCase, ChangeLoggedFilterSetTests):
         )
         Manufacturer.objects.bulk_create(manufacturers)
 
-        device_type = DeviceType.objects.create(manufacturer=manufacturers[0], model='Model 1', slug='model-1')
-        device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+        device_types = (
+            DeviceType(manufacturer=manufacturers[0], model='Device Type 1', slug='device-type-1'),
+            DeviceType(manufacturer=manufacturers[0], model='Device Type 2', slug='device-type-2'),
+            DeviceType(manufacturer=manufacturers[0], model='Device Type 3', slug='device-type-3'),
+        )
+        DeviceType.objects.bulk_create(device_types)
+
+        device_roles = (
+            DeviceRole(name='Device Role 1', slug='device-role-1'),
+            DeviceRole(name='Device Role 2', slug='device-role-2'),
+            DeviceRole(name='Device Role 3', slug='device-role-3'),
+        )
+        DeviceRole.objects.bulk_create(device_roles)
 
         regions = (
             Region(name='Region 1', slug='region-1'),
@@ -3736,9 +3879,9 @@ class InventoryItemTestCase(TestCase, ChangeLoggedFilterSetTests):
         Rack.objects.bulk_create(racks)
 
         devices = (
-            Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], location=locations[0], rack=racks[0]),
-            Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[1], location=locations[1], rack=racks[1]),
-            Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[2], location=locations[2], rack=racks[2]),
+            Device(name='Device 1', device_type=device_types[0], device_role=device_roles[0], site=sites[0], location=locations[0], rack=racks[0]),
+            Device(name='Device 2', device_type=device_types[1], device_role=device_roles[1], site=sites[1], location=locations[1], rack=racks[1]),
+            Device(name='Device 3', device_type=device_types[2], device_role=device_roles[2], site=sites[2], location=locations[2], rack=racks[2]),
         )
         Device.objects.bulk_create(devices)
 
@@ -3829,6 +3972,20 @@ class InventoryItemTestCase(TestCase, ChangeLoggedFilterSetTests):
         params = {'rack': [racks[0].name, racks[1].name]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
 
+    def test_device_type(self):
+        device_types = DeviceType.objects.all()[:2]
+        params = {'device_type_id': [device_types[0].pk, device_types[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'device_type': [device_types[0].model, device_types[1].model]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
+    def test_device_role(self):
+        device_role = DeviceRole.objects.all()[:2]
+        params = {'device_role_id': [device_role[0].pk, device_role[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'device_role': [device_role[0].slug, device_role[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+
     def test_device(self):
         devices = Device.objects.all()[:2]
         params = {'device_id': [devices[0].pk, devices[1].pk]}