From e4b5045ec76b1c751e3e61247fb5f765429ec89f Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 1 Jul 2020 14:55:11 -0400 Subject: [PATCH] #4416: Add bulk_add view for InventoryItems --- netbox/dcim/forms.py | 308 +++++++++++++------------ netbox/dcim/urls.py | 1 + netbox/dcim/views.py | 11 + netbox/templates/dcim/device.html | 3 + netbox/templates/dcim/device_list.html | 1 + 5 files changed, 175 insertions(+), 149 deletions(-) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 2b65940a5..281818895 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -3372,6 +3372,165 @@ class DeviceBayCSVForm(CSVModelForm): self.fields['installed_device'].queryset = Interface.objects.none() +# +# Inventory items +# + +class InventoryItemForm(BootstrapMixin, forms.ModelForm): + device = DynamicModelChoiceField( + queryset=Device.objects.prefetch_related('device_type__manufacturer') + ) + manufacturer = DynamicModelChoiceField( + queryset=Manufacturer.objects.all(), + required=False + ) + tags = DynamicModelMultipleChoiceField( + queryset=Tag.objects.all(), + required=False + ) + + class Meta: + model = InventoryItem + fields = [ + 'name', 'device', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'tags', + ] + + +class InventoryItemCreateForm(BootstrapMixin, forms.Form): + device = DynamicModelChoiceField( + queryset=Device.objects.prefetch_related('device_type__manufacturer') + ) + name_pattern = ExpandableNameField( + label='Name' + ) + manufacturer = DynamicModelChoiceField( + queryset=Manufacturer.objects.all(), + required=False + ) + part_id = forms.CharField( + max_length=50, + required=False, + label='Part ID' + ) + serial = forms.CharField( + max_length=50, + required=False, + ) + asset_tag = forms.CharField( + max_length=50, + required=False, + ) + description = forms.CharField( + max_length=100, + required=False + ) + + +class InventoryItemCSVForm(CSVModelForm): + device = CSVModelChoiceField( + queryset=Device.objects.all(), + to_field_name='name' + ) + manufacturer = CSVModelChoiceField( + queryset=Manufacturer.objects.all(), + to_field_name='name', + required=False + ) + + class Meta: + model = InventoryItem + fields = InventoryItem.csv_headers + + +class InventoryItemBulkCreateForm( + form_from_model(InventoryItem, ['manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', 'tags']), + DeviceBulkAddComponentForm +): + tags = DynamicModelMultipleChoiceField( + queryset=Tag.objects.all(), + required=False + ) + + +class InventoryItemBulkEditForm(BootstrapMixin, BulkEditForm): + pk = forms.ModelMultipleChoiceField( + queryset=InventoryItem.objects.all(), + widget=forms.MultipleHiddenInput() + ) + device = DynamicModelChoiceField( + queryset=Device.objects.all(), + required=False + ) + manufacturer = DynamicModelChoiceField( + queryset=Manufacturer.objects.all(), + required=False + ) + part_id = forms.CharField( + max_length=50, + required=False, + label='Part ID' + ) + description = forms.CharField( + max_length=100, + required=False + ) + + class Meta: + nullable_fields = [ + 'manufacturer', 'part_id', 'description', + ] + + +class InventoryItemFilterForm(BootstrapMixin, forms.Form): + model = InventoryItem + q = forms.CharField( + required=False, + label='Search' + ) + region = DynamicModelMultipleChoiceField( + queryset=Region.objects.all(), + to_field_name='slug', + required=False, + widget=APISelectMultiple( + value_field="slug", + filter_for={ + 'site': 'region' + } + ) + ) + site = DynamicModelMultipleChoiceField( + queryset=Site.objects.all(), + to_field_name='slug', + required=False, + widget=APISelectMultiple( + value_field="slug", + filter_for={ + 'device_id': 'site' + } + ) + ) + device_id = DynamicModelMultipleChoiceField( + queryset=Device.objects.all(), + required=False, + label='Device' + ) + manufacturer = DynamicModelMultipleChoiceField( + queryset=Manufacturer.objects.all(), + to_field_name='slug', + required=False, + widget=APISelect( + value_field="slug", + ) + ) + discovered = forms.NullBooleanField( + required=False, + widget=StaticSelect2( + choices=BOOLEAN_WITH_BLANK_CHOICES + ) + ) + tag = TagFilterField(model) + + # # Cables # @@ -3919,155 +4078,6 @@ class InterfaceConnectionFilterForm(BootstrapMixin, forms.Form): ) -# -# Inventory items -# - -class InventoryItemForm(BootstrapMixin, forms.ModelForm): - device = DynamicModelChoiceField( - queryset=Device.objects.prefetch_related('device_type__manufacturer') - ) - manufacturer = DynamicModelChoiceField( - queryset=Manufacturer.objects.all(), - required=False - ) - tags = DynamicModelMultipleChoiceField( - queryset=Tag.objects.all(), - required=False - ) - - class Meta: - model = InventoryItem - fields = [ - 'name', 'device', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'tags', - ] - - -class InventoryItemCreateForm(BootstrapMixin, forms.Form): - device = DynamicModelChoiceField( - queryset=Device.objects.prefetch_related('device_type__manufacturer') - ) - name_pattern = ExpandableNameField( - label='Name' - ) - manufacturer = DynamicModelChoiceField( - queryset=Manufacturer.objects.all(), - required=False - ) - part_id = forms.CharField( - max_length=50, - required=False, - label='Part ID' - ) - serial = forms.CharField( - max_length=50, - required=False, - ) - asset_tag = forms.CharField( - max_length=50, - required=False, - ) - description = forms.CharField( - max_length=100, - required=False - ) - - -class InventoryItemCSVForm(CSVModelForm): - device = CSVModelChoiceField( - queryset=Device.objects.all(), - to_field_name='name' - ) - manufacturer = CSVModelChoiceField( - queryset=Manufacturer.objects.all(), - to_field_name='name', - required=False - ) - - class Meta: - model = InventoryItem - fields = InventoryItem.csv_headers - - -class InventoryItemBulkEditForm(BootstrapMixin, BulkEditForm): - pk = forms.ModelMultipleChoiceField( - queryset=InventoryItem.objects.all(), - widget=forms.MultipleHiddenInput() - ) - device = DynamicModelChoiceField( - queryset=Device.objects.all(), - required=False - ) - manufacturer = DynamicModelChoiceField( - queryset=Manufacturer.objects.all(), - required=False - ) - part_id = forms.CharField( - max_length=50, - required=False, - label='Part ID' - ) - description = forms.CharField( - max_length=100, - required=False - ) - - class Meta: - nullable_fields = [ - 'manufacturer', 'part_id', 'description', - ] - - -class InventoryItemFilterForm(BootstrapMixin, forms.Form): - model = InventoryItem - q = forms.CharField( - required=False, - label='Search' - ) - region = DynamicModelMultipleChoiceField( - queryset=Region.objects.all(), - to_field_name='slug', - required=False, - widget=APISelectMultiple( - value_field="slug", - filter_for={ - 'site': 'region' - } - ) - ) - site = DynamicModelMultipleChoiceField( - queryset=Site.objects.all(), - to_field_name='slug', - required=False, - widget=APISelectMultiple( - value_field="slug", - filter_for={ - 'device_id': 'site' - } - ) - ) - device_id = DynamicModelMultipleChoiceField( - queryset=Device.objects.all(), - required=False, - label='Device' - ) - manufacturer = DynamicModelMultipleChoiceField( - queryset=Manufacturer.objects.all(), - to_field_name='slug', - required=False, - widget=APISelect( - value_field="slug", - ) - ) - discovered = forms.NullBooleanField( - required=False, - widget=StaticSelect2( - choices=BOOLEAN_WITH_BLANK_CHOICES - ) - ) - tag = TagFilterField(model) - - # # Virtual chassis # diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index 53d65502c..831d40402 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -326,6 +326,7 @@ urlpatterns = [ path('inventory-items//edit/', views.InventoryItemEditView.as_view(), name='inventoryitem_edit'), path('inventory-items//delete/', views.InventoryItemDeleteView.as_view(), name='inventoryitem_delete'), path('inventory-items//changelog/', ObjectChangeLogView.as_view(), name='inventoryitem_changelog', kwargs={'model': InventoryItem}), + path('devices/inventory-items/add/', views.DeviceBulkAddInventoryItemView.as_view(), name='device_bulk_add_inventoryitem'), # Cables path('cables/', views.CableListView.as_view(), name='cable_list'), diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 054d3de34..4456e408d 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1859,6 +1859,17 @@ class DeviceBulkAddDeviceBayView(BulkComponentCreateView): default_return_url = 'dcim:device_list' +class DeviceBulkAddInventoryItemView(BulkComponentCreateView): + parent_model = Device + parent_field = 'device' + form = forms.InventoryItemBulkCreateForm + queryset = InventoryItem.objects.all() + model_form = forms.InventoryItemForm + filterset = filters.DeviceFilterSet + table = tables.DeviceTable + default_return_url = 'dcim:device_list' + + # # Cables # diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 03ca0f8b4..710288da6 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -74,6 +74,9 @@ {% if perms.dcim.add_devicebay %}
  • Device Bays
  • {% endif %} + {% if perms.dcim.add_inventoryitem %} +
  • Inventory Items
  • + {% endif %} {% endif %} diff --git a/netbox/templates/dcim/device_list.html b/netbox/templates/dcim/device_list.html index f236a0550..b1cd32eea 100644 --- a/netbox/templates/dcim/device_list.html +++ b/netbox/templates/dcim/device_list.html @@ -14,6 +14,7 @@ {% if perms.dcim.add_interface %}
  • Interfaces
  • {% endif %} {% if perms.dcim.add_rearport %}
  • Rear Ports
  • {% endif %} {% if perms.dcim.add_devicebay %}
  • Device Bays
  • {% endif %} + {% if perms.dcim.add_inventoryitem %}
  • Inventory Items
  • {% endif %} {% endif %}