فهرست منبع

#8037: Add role field to InventoryItem

jeremystretch 4 سال پیش
والد
کامیت
6e9afccfd7

+ 1 - 1
docs/models/dcim/inventoryitem.md

@@ -2,6 +2,6 @@
 
 
 Inventory items represent hardware components installed within a device, such as a power supply or CPU or line card. Inventory items are distinct from other device components in that they cannot be templatized on a device type, and cannot be connected by cables. They are intended to be used primarily for inventory purposes.
 Inventory items represent hardware components installed within a device, such as a power supply or CPU or line card. Inventory items are distinct from other device components in that they cannot be templatized on a device type, and cannot be connected by cables. They are intended to be used primarily for inventory purposes.
 
 
-Each inventory item can be assigned a manufacturer, part ID, serial number, and asset tag (all optional). A boolean toggle is also provided to indicate whether each item was entered manually or discovered automatically (by some process outside of NetBox).
+Each inventory item can be assigned a functional role, manufacturer, part ID, serial number, and asset tag (all optional). A boolean toggle is also provided to indicate whether each item was entered manually or discovered automatically (by some process outside of NetBox).
 
 
 Inventory items are hierarchical in nature, such that any individual item may be designated as the parent for other items. For example, an inventory item might be created to represent a line card which houses several SFP optics, each of which exists as a child item within the device.
 Inventory items are hierarchical in nature, such that any individual item may be designated as the parent for other items. For example, an inventory item might be created to represent a line card which houses several SFP optics, each of which exists as a child item within the device.

+ 2 - 1
netbox/dcim/api/serializers.py

@@ -811,12 +811,13 @@ class InventoryItemSerializer(PrimaryModelSerializer):
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
     parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
     manufacturer = NestedManufacturerSerializer(required=False, allow_null=True, default=None)
     manufacturer = NestedManufacturerSerializer(required=False, allow_null=True, default=None)
+    role = NestedInventoryItemRoleSerializer(required=False, allow_null=True)
     _depth = serializers.IntegerField(source='level', read_only=True)
     _depth = serializers.IntegerField(source='level', read_only=True)
 
 
     class Meta:
     class Meta:
         model = InventoryItem
         model = InventoryItem
         fields = [
         fields = [
-            'id', 'url', 'display', 'device', 'parent', 'name', 'label', 'manufacturer', 'part_id', 'serial',
+            'id', 'url', 'display', 'device', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial',
             'asset_tag', 'discovered', 'description', 'tags', 'custom_fields', 'created', 'last_updated', '_depth',
             'asset_tag', 'discovered', 'description', 'tags', 'custom_fields', 'created', 'last_updated', '_depth',
         ]
         ]
 
 

+ 10 - 0
netbox/dcim/filtersets.py

@@ -1284,6 +1284,16 @@ class InventoryItemFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Manufacturer (slug)',
         label='Manufacturer (slug)',
     )
     )
+    role_id = django_filters.ModelMultipleChoiceFilter(
+        queryset=InventoryItemRole.objects.all(),
+        label='Role (ID)',
+    )
+    role = django_filters.ModelMultipleChoiceFilter(
+        field_name='role__slug',
+        queryset=InventoryItemRole.objects.all(),
+        to_field_name='slug',
+        label='Role (slug)',
+    )
     serial = django_filters.CharFilter(
     serial = django_filters.CharFilter(
         lookup_expr='iexact'
         lookup_expr='iexact'
     )
     )

+ 3 - 3
netbox/dcim/forms/bulk_create.py

@@ -107,11 +107,11 @@ class DeviceBayBulkCreateForm(DeviceBulkAddComponentForm):
 
 
 
 
 class InventoryItemBulkCreateForm(
 class InventoryItemBulkCreateForm(
-    form_from_model(InventoryItem, ['manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered']),
+    form_from_model(InventoryItem, ['role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered']),
     DeviceBulkAddComponentForm
     DeviceBulkAddComponentForm
 ):
 ):
     model = InventoryItem
     model = InventoryItem
     field_order = (
     field_order = (
-        'name_pattern', 'label_pattern', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', 'description',
-        'tags',
+        'name_pattern', 'label_pattern', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered',
+        'description', 'tags',
     )
     )

+ 6 - 2
netbox/dcim/forms/bulk_edit.py

@@ -1172,7 +1172,7 @@ class DeviceBayBulkEditForm(
 
 
 
 
 class InventoryItemBulkEditForm(
 class InventoryItemBulkEditForm(
-    form_from_model(InventoryItem, ['label', 'manufacturer', 'part_id', 'description']),
+    form_from_model(InventoryItem, ['label', 'role', 'manufacturer', 'part_id', 'description']),
     AddRemoveTagsForm,
     AddRemoveTagsForm,
     CustomFieldModelBulkEditForm
     CustomFieldModelBulkEditForm
 ):
 ):
@@ -1180,13 +1180,17 @@ class InventoryItemBulkEditForm(
         queryset=InventoryItem.objects.all(),
         queryset=InventoryItem.objects.all(),
         widget=forms.MultipleHiddenInput()
         widget=forms.MultipleHiddenInput()
     )
     )
+    role = DynamicModelChoiceField(
+        queryset=InventoryItemRole.objects.all(),
+        required=False
+    )
     manufacturer = DynamicModelChoiceField(
     manufacturer = DynamicModelChoiceField(
         queryset=Manufacturer.objects.all(),
         queryset=Manufacturer.objects.all(),
         required=False
         required=False
     )
     )
 
 
     class Meta:
     class Meta:
-        nullable_fields = ['label', 'manufacturer', 'part_id', 'description']
+        nullable_fields = ['label', 'role', 'manufacturer', 'part_id', 'description']
 
 
 
 
 #
 #

+ 7 - 1
netbox/dcim/forms/bulk_import.py

@@ -772,6 +772,11 @@ class InventoryItemCSVForm(CustomFieldModelCSVForm):
         queryset=Device.objects.all(),
         queryset=Device.objects.all(),
         to_field_name='name'
         to_field_name='name'
     )
     )
+    role = CSVModelChoiceField(
+        queryset=InventoryItemRole.objects.all(),
+        to_field_name='name',
+        required=False
+    )
     manufacturer = CSVModelChoiceField(
     manufacturer = CSVModelChoiceField(
         queryset=Manufacturer.objects.all(),
         queryset=Manufacturer.objects.all(),
         to_field_name='name',
         to_field_name='name',
@@ -787,7 +792,8 @@ class InventoryItemCSVForm(CustomFieldModelCSVForm):
     class Meta:
     class Meta:
         model = InventoryItem
         model = InventoryItem
         fields = (
         fields = (
-            'device', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', 'description',
+            'device', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered',
+            'description',
         )
         )
 
 
     def __init__(self, *args, **kwargs):
     def __init__(self, *args, **kwargs):

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

@@ -1100,6 +1100,12 @@ class InventoryItemFilterForm(DeviceComponentFilterForm):
         ['name', 'label', 'manufacturer_id', 'serial', 'asset_tag', 'discovered'],
         ['name', 'label', 'manufacturer_id', 'serial', 'asset_tag', 'discovered'],
         ['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
         ['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
     ]
     ]
+    role_id = DynamicModelMultipleChoiceField(
+        queryset=InventoryItemRole.objects.all(),
+        required=False,
+        label=_('Role'),
+        fetch_trigger='open'
+    )
     manufacturer_id = DynamicModelMultipleChoiceField(
     manufacturer_id = DynamicModelMultipleChoiceField(
         queryset=Manufacturer.objects.all(),
         queryset=Manufacturer.objects.all(),
         required=False,
         required=False,

+ 6 - 2
netbox/dcim/forms/models.py

@@ -1368,6 +1368,10 @@ class InventoryItemForm(CustomFieldModelForm):
             'device_id': '$device'
             'device_id': '$device'
         }
         }
     )
     )
+    role = DynamicModelChoiceField(
+        queryset=InventoryItemRole.objects.all(),
+        required=False
+    )
     manufacturer = DynamicModelChoiceField(
     manufacturer = DynamicModelChoiceField(
         queryset=Manufacturer.objects.all(),
         queryset=Manufacturer.objects.all(),
         required=False
         required=False
@@ -1380,8 +1384,8 @@ class InventoryItemForm(CustomFieldModelForm):
     class Meta:
     class Meta:
         model = InventoryItem
         model = InventoryItem
         fields = [
         fields = [
-            'device', 'parent', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description',
-            'tags',
+            'device', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
+            'description', 'tags',
         ]
         ]
 
 
 
 

+ 9 - 5
netbox/dcim/forms/object_create.py

@@ -652,10 +652,6 @@ class DeviceBayCreateForm(ComponentCreateForm):
 
 
 class InventoryItemCreateForm(ComponentCreateForm):
 class InventoryItemCreateForm(ComponentCreateForm):
     model = InventoryItem
     model = InventoryItem
-    manufacturer = DynamicModelChoiceField(
-        queryset=Manufacturer.objects.all(),
-        required=False
-    )
     parent = DynamicModelChoiceField(
     parent = DynamicModelChoiceField(
         queryset=InventoryItem.objects.all(),
         queryset=InventoryItem.objects.all(),
         required=False,
         required=False,
@@ -663,6 +659,14 @@ class InventoryItemCreateForm(ComponentCreateForm):
             'device_id': '$device'
             'device_id': '$device'
         }
         }
     )
     )
+    role = DynamicModelChoiceField(
+        queryset=InventoryItemRole.objects.all(),
+        required=False
+    )
+    manufacturer = DynamicModelChoiceField(
+        queryset=Manufacturer.objects.all(),
+        required=False
+    )
     part_id = forms.CharField(
     part_id = forms.CharField(
         max_length=50,
         max_length=50,
         required=False,
         required=False,
@@ -677,6 +681,6 @@ class InventoryItemCreateForm(ComponentCreateForm):
         required=False,
         required=False,
     )
     )
     field_order = (
     field_order = (
-        'device', 'parent', 'name_pattern', 'label_pattern', 'manufacturer', 'part_id', 'serial', 'asset_tag',
+        'device', 'parent', 'name_pattern', 'label_pattern', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
         'description', 'tags',
         'description', 'tags',
     )
     )

+ 8 - 8
netbox/dcim/models/device_components.py

@@ -994,6 +994,13 @@ class InventoryItem(MPTTModel, ComponentModel):
         null=True,
         null=True,
         db_index=True
         db_index=True
     )
     )
+    role = models.ForeignKey(
+        to='dcim.InventoryItemRole',
+        on_delete=models.PROTECT,
+        related_name='inventory_items',
+        blank=True,
+        null=True
+    )
     manufacturer = models.ForeignKey(
     manufacturer = models.ForeignKey(
         to='dcim.Manufacturer',
         to='dcim.Manufacturer',
         on_delete=models.PROTECT,
         on_delete=models.PROTECT,
@@ -1007,13 +1014,6 @@ class InventoryItem(MPTTModel, ComponentModel):
         blank=True,
         blank=True,
         help_text='Manufacturer-assigned part identifier'
         help_text='Manufacturer-assigned part identifier'
     )
     )
-    role = models.ForeignKey(
-        to='dcim.InventoryItemRole',
-        on_delete=models.PROTECT,
-        related_name='inventory_items',
-        blank=True,
-        null=True
-    )
     serial = models.CharField(
     serial = models.CharField(
         max_length=50,
         max_length=50,
         verbose_name='Serial number',
         verbose_name='Serial number',
@@ -1034,7 +1034,7 @@ class InventoryItem(MPTTModel, ComponentModel):
 
 
     objects = TreeManager()
     objects = TreeManager()
 
 
-    clone_fields = ['device', 'parent', 'manufacturer', 'part_id', 'role']
+    clone_fields = ['device', 'parent', 'role', 'manufacturer', 'part_id']
 
 
     class Meta:
     class Meta:
         ordering = ('device__id', 'parent__id', '_name')
         ordering = ('device__id', 'parent__id', '_name')

+ 29 - 27
netbox/dcim/tables/devices.py

@@ -774,6 +774,9 @@ class InventoryItemTable(DeviceComponentTable):
             'args': [Accessor('device_id')],
             'args': [Accessor('device_id')],
         }
         }
     )
     )
+    role = tables.Column(
+        linkify=True
+    )
     manufacturer = tables.Column(
     manufacturer = tables.Column(
         linkify=True
         linkify=True
     )
     )
@@ -786,10 +789,33 @@ class InventoryItemTable(DeviceComponentTable):
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = InventoryItem
         model = InventoryItem
         fields = (
         fields = (
-            'pk', 'id', 'name', 'device', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description',
-            'discovered', 'tags',
+            'pk', 'id', 'name', 'device', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
+            'description', 'discovered', 'tags',
+        )
+        default_columns = ('pk', 'name', 'device', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag')
+
+
+class DeviceInventoryItemTable(InventoryItemTable):
+    name = tables.TemplateColumn(
+        template_code='<a href="{{ record.get_absolute_url }}" style="padding-left: {{ record.level }}0px">'
+                      '{{ value }}</a>',
+        order_by=Accessor('_name'),
+        attrs={'td': {'class': 'text-nowrap'}}
+    )
+    actions = ButtonsColumn(
+        model=InventoryItem,
+        buttons=('edit', 'delete')
+    )
+
+    class Meta(BaseTable.Meta):
+        model = InventoryItem
+        fields = (
+            'pk', 'id', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description',
+            'discovered', 'tags', 'actions',
+        )
+        default_columns = (
+            'pk', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'actions',
         )
         )
-        default_columns = ('pk', 'name', 'device', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag')
 
 
 
 
 class InventoryItemRoleTable(BaseTable):
 class InventoryItemRoleTable(BaseTable):
@@ -816,30 +842,6 @@ class InventoryItemRoleTable(BaseTable):
         default_columns = ('pk', 'name', 'inventoryitem_count', 'color', 'description', 'actions')
         default_columns = ('pk', 'name', 'inventoryitem_count', 'color', 'description', 'actions')
 
 
 
 
-class DeviceInventoryItemTable(InventoryItemTable):
-    name = tables.TemplateColumn(
-        template_code='<a href="{{ record.get_absolute_url }}" style="padding-left: {{ record.level }}0px">'
-                      '{{ value }}</a>',
-        order_by=Accessor('_name'),
-        attrs={'td': {'class': 'text-nowrap'}}
-    )
-    actions = ButtonsColumn(
-        model=InventoryItem,
-        buttons=('edit', 'delete')
-    )
-
-    class Meta(BaseTable.Meta):
-        model = InventoryItem
-        fields = (
-            'pk', 'id', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'discovered',
-            'tags', 'actions',
-        )
-        default_columns = (
-            'pk', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'discovered',
-            'actions',
-        )
-
-
 #
 #
 # Virtual chassis
 # Virtual chassis
 #
 #

+ 12 - 3
netbox/dcim/tests/test_api.py

@@ -1626,24 +1626,33 @@ class InventoryItemTest(APIViewTestCases.APIViewTestCase):
         devicerole = DeviceRole.objects.create(name='Test Device Role 1', slug='test-device-role-1', color='ff0000')
         devicerole = DeviceRole.objects.create(name='Test Device Role 1', slug='test-device-role-1', color='ff0000')
         device = Device.objects.create(device_type=devicetype, device_role=devicerole, name='Device 1', site=site)
         device = Device.objects.create(device_type=devicetype, device_role=devicerole, name='Device 1', site=site)
 
 
-        InventoryItem.objects.create(device=device, name='Inventory Item 1', manufacturer=manufacturer)
-        InventoryItem.objects.create(device=device, name='Inventory Item 2', manufacturer=manufacturer)
-        InventoryItem.objects.create(device=device, name='Inventory Item 3', manufacturer=manufacturer)
+        roles = (
+            InventoryItemRole(name='Inventory Item Role 1', slug='inventory-item-role-1'),
+            InventoryItemRole(name='Inventory Item Role 2', slug='inventory-item-role-2'),
+        )
+        InventoryItemRole.objects.bulk_create(roles)
+
+        InventoryItem.objects.create(device=device, name='Inventory Item 1', role=roles[0], manufacturer=manufacturer)
+        InventoryItem.objects.create(device=device, name='Inventory Item 2', role=roles[0], manufacturer=manufacturer)
+        InventoryItem.objects.create(device=device, name='Inventory Item 3', role=roles[0], manufacturer=manufacturer)
 
 
         cls.create_data = [
         cls.create_data = [
             {
             {
                 'device': device.pk,
                 'device': device.pk,
                 'name': 'Inventory Item 4',
                 'name': 'Inventory Item 4',
+                'role': roles[1].pk,
                 'manufacturer': manufacturer.pk,
                 'manufacturer': manufacturer.pk,
             },
             },
             {
             {
                 'device': device.pk,
                 'device': device.pk,
                 'name': 'Inventory Item 5',
                 'name': 'Inventory Item 5',
+                'role': roles[1].pk,
                 'manufacturer': manufacturer.pk,
                 'manufacturer': manufacturer.pk,
             },
             },
             {
             {
                 'device': device.pk,
                 'device': device.pk,
                 'name': 'Inventory Item 6',
                 'name': 'Inventory Item 6',
+                'role': roles[1].pk,
                 'manufacturer': manufacturer.pk,
                 'manufacturer': manufacturer.pk,
             },
             },
         ]
         ]

+ 17 - 4
netbox/dcim/tests/test_filtersets.py

@@ -2949,7 +2949,6 @@ class InventoryItemTestCase(TestCase, ChangeLoggedFilterSetTests):
 
 
     @classmethod
     @classmethod
     def setUpTestData(cls):
     def setUpTestData(cls):
-
         manufacturers = (
         manufacturers = (
             Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
             Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
             Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
             Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
@@ -2998,10 +2997,17 @@ class InventoryItemTestCase(TestCase, ChangeLoggedFilterSetTests):
         )
         )
         Device.objects.bulk_create(devices)
         Device.objects.bulk_create(devices)
 
 
+        roles = (
+            InventoryItemRole(name='Inventory Item Role 1', slug='inventory-item-role-1'),
+            InventoryItemRole(name='Inventory Item Role 2', slug='inventory-item-role-2'),
+            InventoryItemRole(name='Inventory Item Role 3', slug='inventory-item-role-3'),
+        )
+        InventoryItemRole.objects.bulk_create(roles)
+
         inventory_items = (
         inventory_items = (
-            InventoryItem(device=devices[0], manufacturer=manufacturers[0], name='Inventory Item 1', label='A', part_id='1001', serial='ABC', asset_tag='1001', discovered=True, description='First'),
-            InventoryItem(device=devices[1], manufacturer=manufacturers[1], name='Inventory Item 2', label='B', part_id='1002', serial='DEF', asset_tag='1002', discovered=True, description='Second'),
-            InventoryItem(device=devices[2], manufacturer=manufacturers[2], name='Inventory Item 3', label='C', part_id='1003', serial='GHI', asset_tag='1003', discovered=False, description='Third'),
+            InventoryItem(device=devices[0], role=roles[0], manufacturer=manufacturers[0], name='Inventory Item 1', label='A', part_id='1001', serial='ABC', asset_tag='1001', discovered=True, description='First'),
+            InventoryItem(device=devices[1], role=roles[1], manufacturer=manufacturers[1], name='Inventory Item 2', label='B', part_id='1002', serial='DEF', asset_tag='1002', discovered=True, description='Second'),
+            InventoryItem(device=devices[2], role=roles[2], manufacturer=manufacturers[2], name='Inventory Item 3', label='C', part_id='1003', serial='GHI', asset_tag='1003', discovered=False, description='Third'),
         )
         )
         for i in inventory_items:
         for i in inventory_items:
             i.save()
             i.save()
@@ -3077,6 +3083,13 @@ class InventoryItemTestCase(TestCase, ChangeLoggedFilterSetTests):
         params = {'parent_id': [parent_items[0].pk, parent_items[1].pk]}
         params = {'parent_id': [parent_items[0].pk, parent_items[1].pk]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
 
+    def test_role(self):
+        roles = InventoryItemRole.objects.all()[:2]
+        params = {'role_id': [roles[0].pk, roles[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+        params = {'role': [roles[0].slug, roles[1].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
     def test_manufacturer(self):
     def test_manufacturer(self):
         manufacturers = Manufacturer.objects.all()[:2]
         manufacturers = Manufacturer.objects.all()[:2]
         params = {'manufacturer_id': [manufacturers[0].pk, manufacturers[1].pk]}
         params = {'manufacturer_id': [manufacturers[0].pk, manufacturers[1].pk]}

+ 12 - 3
netbox/dcim/tests/test_views.py

@@ -2331,14 +2331,21 @@ class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
         device = create_test_device('Device 1')
         device = create_test_device('Device 1')
         manufacturer, _ = Manufacturer.objects.get_or_create(name='Manufacturer 1', slug='manufacturer-1')
         manufacturer, _ = Manufacturer.objects.get_or_create(name='Manufacturer 1', slug='manufacturer-1')
 
 
-        InventoryItem.objects.create(device=device, name='Inventory Item 1')
-        InventoryItem.objects.create(device=device, name='Inventory Item 2')
-        InventoryItem.objects.create(device=device, name='Inventory Item 3')
+        roles = (
+            InventoryItemRole(name='Inventory Item Role 1', slug='inventory-item-role-1'),
+            InventoryItemRole(name='Inventory Item Role 2', slug='inventory-item-role-2'),
+        )
+        InventoryItemRole.objects.bulk_create(roles)
+
+        InventoryItem.objects.create(device=device, name='Inventory Item 1', role=roles[0], manufacturer=manufacturer)
+        InventoryItem.objects.create(device=device, name='Inventory Item 2', role=roles[0], manufacturer=manufacturer)
+        InventoryItem.objects.create(device=device, name='Inventory Item 3', role=roles[0], manufacturer=manufacturer)
 
 
         tags = create_tags('Alpha', 'Bravo', 'Charlie')
         tags = create_tags('Alpha', 'Bravo', 'Charlie')
 
 
         cls.form_data = {
         cls.form_data = {
             'device': device.pk,
             'device': device.pk,
+            'role': roles[1].pk,
             'manufacturer': manufacturer.pk,
             'manufacturer': manufacturer.pk,
             'name': 'Inventory Item X',
             'name': 'Inventory Item X',
             'parent': None,
             'parent': None,
@@ -2353,6 +2360,7 @@ class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
         cls.bulk_create_data = {
         cls.bulk_create_data = {
             'device': device.pk,
             'device': device.pk,
             'name_pattern': 'Inventory Item [4-6]',
             'name_pattern': 'Inventory Item [4-6]',
+            'role': roles[1].pk,
             'manufacturer': manufacturer.pk,
             'manufacturer': manufacturer.pk,
             'parent': None,
             'parent': None,
             'discovered': False,
             'discovered': False,
@@ -2363,6 +2371,7 @@ class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
         }
         }
 
 
         cls.bulk_edit_data = {
         cls.bulk_edit_data = {
+            'role': roles[1].pk,
             'part_id': '123456',
             'part_id': '123456',
             'description': 'New description',
             'description': 'New description',
         }
         }

+ 2 - 2
netbox/dcim/views.py

@@ -2412,7 +2412,7 @@ class InventoryItemBulkImportView(generic.BulkImportView):
 
 
 
 
 class InventoryItemBulkEditView(generic.BulkEditView):
 class InventoryItemBulkEditView(generic.BulkEditView):
-    queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer')
+    queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer', 'role')
     filterset = filtersets.InventoryItemFilterSet
     filterset = filtersets.InventoryItemFilterSet
     table = tables.InventoryItemTable
     table = tables.InventoryItemTable
     form = forms.InventoryItemBulkEditForm
     form = forms.InventoryItemBulkEditForm
@@ -2423,7 +2423,7 @@ class InventoryItemBulkRenameView(generic.BulkRenameView):
 
 
 
 
 class InventoryItemBulkDeleteView(generic.BulkDeleteView):
 class InventoryItemBulkDeleteView(generic.BulkDeleteView):
-    queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer')
+    queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer', 'role')
     table = tables.InventoryItemTable
     table = tables.InventoryItemTable
     template_name = 'dcim/inventoryitem_bulk_delete.html'
     template_name = 'dcim/inventoryitem_bulk_delete.html'
 
 

+ 11 - 3
netbox/templates/dcim/inventoryitem.html

@@ -13,9 +13,7 @@
     <div class="row mb-3">
     <div class="row mb-3">
         <div class="col col-md-6">
         <div class="col col-md-6">
             <div class="card">
             <div class="card">
-                <h5 class="card-header">
-                    Inventory Item
-                </h5>
+                <h5 class="card-header">Inventory Item</h5>
                 <div class="card-body">
                 <div class="card-body">
                     <table class="table table-hover attr-table">
                     <table class="table table-hover attr-table">
                         <tr>
                         <tr>
@@ -42,6 +40,16 @@
                             <th scope="row">Label</th>
                             <th scope="row">Label</th>
                             <td>{{ object.label|placeholder }}</td>
                             <td>{{ object.label|placeholder }}</td>
                         </tr>
                         </tr>
+                        <tr>
+                            <th scope="row">Role</th>
+                            <td>
+                                {% if object.role %}
+                                    <a href="{{ object.role.get_absolute_url }}">{{ object.role }}</a>
+                                {% else %}
+                                    <span class="text-muted">&mdash;</span>
+                                {% endif %}
+                            </td>
+                        </tr>
                         <tr>
                         <tr>
                             <th scope="row">Manufacturer</th>
                             <th scope="row">Manufacturer</th>
                             <td>
                             <td>