1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Merge branch 'develop' into feature

This commit is contained in:
Jeremy Stretch
2024-04-22 13:23:42 -04:00
67 changed files with 3840 additions and 3326 deletions

View File

@@ -1373,14 +1373,14 @@ class VirtualDeviceContextImportForm(NetBoxModelImportForm):
label=_('Device'),
queryset=Device.objects.all(),
to_field_name='name',
help_text='Assigned role'
help_text=_('Assigned role')
)
tenant = CSVModelChoiceField(
label=_('Tenant'),
queryset=Tenant.objects.all(),
required=False,
to_field_name='name',
help_text='Assigned tenant'
help_text=_('Assigned tenant')
)
status = CSVChoiceField(
label=_('Status'),

View File

@@ -975,9 +975,9 @@ class CableFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
label=_('Color'),
required=False
)
length = forms.IntegerField(
length = forms.DecimalField(
label=_('Length'),
required=False
required=False,
)
length_unit = forms.ChoiceField(
label=_('Length unit'),

View File

@@ -13,8 +13,7 @@ from netbox.forms import NetBoxModelForm
from tenancy.forms import TenancyForm
from utilities.forms import add_blank_choice
from utilities.forms.fields import (
CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField,
NumericArrayField, SlugField,
CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField, NumericArrayField, SlugField,
)
from utilities.forms.rendering import FieldSet, InlineFields, TabbedGroups
from utilities.forms.widgets import APISelect, ClearableFileInput, HTMXSelect, NumberWithOptions, SelectWithPK
@@ -1003,15 +1002,62 @@ class InventoryItemTemplateForm(ComponentTemplateForm):
queryset=Manufacturer.objects.all(),
required=False
)
component_type = ContentTypeChoiceField(
queryset=ContentType.objects.all(),
limit_choices_to=MODULAR_COMPONENT_TEMPLATE_MODELS,
# Assigned component selectors
consoleporttemplate = DynamicModelChoiceField(
queryset=ConsolePortTemplate.objects.all(),
required=False,
widget=forms.HiddenInput
query_params={
'device_type_id': '$device_type'
},
label=_('Console port template')
)
component_id = forms.IntegerField(
consoleserverporttemplate = DynamicModelChoiceField(
queryset=ConsoleServerPortTemplate.objects.all(),
required=False,
widget=forms.HiddenInput
query_params={
'device_type_id': '$device_type'
},
label=_('Console server port template')
)
frontporttemplate = DynamicModelChoiceField(
queryset=FrontPortTemplate.objects.all(),
required=False,
query_params={
'device_type_id': '$device_type'
},
label=_('Front port template')
)
interfacetemplate = DynamicModelChoiceField(
queryset=InterfaceTemplate.objects.all(),
required=False,
query_params={
'device_type_id': '$device_type'
},
label=_('Interface template')
)
poweroutlettemplate = DynamicModelChoiceField(
queryset=PowerOutletTemplate.objects.all(),
required=False,
query_params={
'device_type_id': '$device_type'
},
label=_('Power outlet template')
)
powerporttemplate = DynamicModelChoiceField(
queryset=PowerPortTemplate.objects.all(),
required=False,
query_params={
'device_type_id': '$device_type'
},
label=_('Power port template')
)
rearporttemplate = DynamicModelChoiceField(
queryset=RearPortTemplate.objects.all(),
required=False,
query_params={
'device_type_id': '$device_type'
},
label=_('Rear port template')
)
fieldsets = (
@@ -1025,9 +1071,52 @@ class InventoryItemTemplateForm(ComponentTemplateForm):
model = InventoryItemTemplate
fields = [
'device_type', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'description',
'component_type', 'component_id',
]
def __init__(self, *args, **kwargs):
instance = kwargs.get('instance')
initial = kwargs.get('initial', {}).copy()
component_type = initial.get('component_type')
component_id = initial.get('component_id')
# Used for picking the default active tab for component selection
self.no_component = True
if instance:
# When editing set the initial value for component selection
for component_model in ContentType.objects.filter(MODULAR_COMPONENT_TEMPLATE_MODELS):
if type(instance.component) is component_model.model_class():
initial[component_model.model] = instance.component
self.no_component = False
break
elif component_type and component_id:
# When adding the InventoryItem from a component page
if content_type := ContentType.objects.filter(MODULAR_COMPONENT_TEMPLATE_MODELS).filter(pk=component_type).first():
if component := content_type.model_class().objects.filter(pk=component_id).first():
initial[content_type.model] = component
self.no_component = False
kwargs['initial'] = initial
super().__init__(*args, **kwargs)
def clean(self):
super().clean()
# Handle object assignment
selected_objects = [
field for field in (
'consoleporttemplate', 'consoleserverporttemplate', 'frontporttemplate', 'interfacetemplate',
'poweroutlettemplate', 'powerporttemplate', 'rearporttemplate'
) if self.cleaned_data[field]
]
if len(selected_objects) > 1:
raise forms.ValidationError(_("An InventoryItem can only be assigned to a single component."))
elif selected_objects:
self.instance.component = self.cleaned_data[selected_objects[0]]
else:
self.instance.component = None
#
# Device components