UI cleanup for front/rear ports

This commit is contained in:
Jeremy Stretch
2025-11-21 14:03:16 -05:00
parent 85d4066501
commit 62620101db
5 changed files with 52 additions and 27 deletions

View File

@@ -1119,7 +1119,7 @@ class FrontPortTemplateForm(FrontPortFormMixin, ModularComponentTemplateForm):
FieldSet('device_type', name=_('Device Type')),
FieldSet('module_type', name=_('Module Type')),
),
'name', 'label', 'positions', 'rear_ports', 'description',
'name', 'label', 'type', 'positions', 'rear_ports', 'description',
),
)

View File

@@ -133,9 +133,10 @@ class FrontPortTemplateCreateForm(ComponentCreateForm, model_forms.FrontPortTemp
# Check that the number of FrontPortTemplates to be created matches the selected number of RearPortTemplate
# positions
positions = self.cleaned_data['positions']
frontport_count = len(self.cleaned_data['name'])
rearport_count = len(self.cleaned_data['rear_ports'])
if frontport_count != rearport_count:
if frontport_count * positions != rearport_count:
raise forms.ValidationError({
'rear_ports': _(
"The number of front port templates to be created ({frontport_count}) must match the selected "
@@ -251,10 +252,11 @@ class FrontPortCreateForm(ComponentCreateForm, model_forms.FrontPortForm):
def clean(self):
super(NetBoxModelForm, self).clean()
# Check that the number of FrontPorts to be created matches the selected number of RearPort positions
# Check that the number of FrontPorts to be created matches the selected number of RearPorts
positions = self.cleaned_data['positions']
frontport_count = len(self.cleaned_data['name'])
rearport_count = len(self.cleaned_data['rear_ports'])
if frontport_count != rearport_count:
if frontport_count * positions != rearport_count:
raise forms.ValidationError({
'rear_ports': _(
"The number of front ports to be created ({frontport_count}) must match the selected number of "

View File

@@ -635,6 +635,20 @@ class RearPortTemplate(ModularComponentTemplateModel):
verbose_name = _('rear port template')
verbose_name_plural = _('rear port templates')
def clean(self):
super().clean()
# Check that positions count is greater than or equal to the number of associated FrontPortTemplates
if not self._state.adding:
assignment_count = self.assignments.count()
if self.positions < assignment_count:
raise ValidationError({
"positions": _(
"The number of positions cannot be less than the number of mapped front port templates "
"({count})"
).format(count=assignment_count)
})
def instantiate(self, **kwargs):
return self.component_model(
name=self.resolve_name(kwargs.get('module')),

View File

@@ -749,12 +749,9 @@ class FrontPortTable(ModularDeviceComponentTable, CableTerminationTable):
color = columns.ColorColumn(
verbose_name=_('Color'),
)
rear_port_position = tables.Column(
verbose_name=_('Position')
)
rear_port = tables.Column(
verbose_name=_('Rear Port'),
linkify=True
assignments = columns.ManyToManyColumn(
verbose_name=_('Assignments'),
transform=lambda obj: f'{obj.rear_port}:{obj.rear_port_position}'
)
tags = columns.TagColumn(
url_name='dcim:frontport_list'
@@ -763,12 +760,12 @@ class FrontPortTable(ModularDeviceComponentTable, CableTerminationTable):
class Meta(DeviceComponentTable.Meta):
model = models.FrontPort
fields = (
'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'color', 'positions', 'description',
'mark_connected', 'cable', 'cable_color', 'link_peer',
'inventory_items', 'tags', 'created', 'last_updated',
'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'color', 'positions', 'assignments',
'description', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'inventory_items', 'tags', 'created',
'last_updated',
)
default_columns = (
'pk', 'name', 'device', 'label', 'type', 'color', 'positions', 'description',
'pk', 'name', 'device', 'label', 'type', 'color', 'positions', 'assignments', 'description',
)
@@ -786,11 +783,11 @@ class DeviceFrontPortTable(FrontPortTable):
class Meta(CableTerminationTable.Meta, DeviceComponentTable.Meta):
model = models.FrontPort
fields = (
'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'rear_port', 'rear_port_position',
'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'color', 'positions', 'assignments',
'description', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'tags', 'actions',
)
default_columns = (
'pk', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', 'cable', 'link_peer',
'pk', 'name', 'label', 'type', 'color', 'positions', 'assignments', 'description', 'cable', 'link_peer',
)
@@ -805,6 +802,10 @@ class RearPortTable(ModularDeviceComponentTable, CableTerminationTable):
color = columns.ColorColumn(
verbose_name=_('Color'),
)
assignments = columns.ManyToManyColumn(
verbose_name=_('Assignments'),
transform=lambda obj: f'{obj.front_port}:{obj.front_port_position}'
)
tags = columns.TagColumn(
url_name='dcim:rearport_list'
)
@@ -812,10 +813,13 @@ class RearPortTable(ModularDeviceComponentTable, CableTerminationTable):
class Meta(DeviceComponentTable.Meta):
model = models.RearPort
fields = (
'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'color', 'positions', 'description',
'mark_connected', 'cable', 'cable_color', 'link_peer', 'inventory_items', 'tags', 'created', 'last_updated',
'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'color', 'positions', 'assignments',
'description', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'inventory_items', 'tags', 'created',
'last_updated',
)
default_columns = (
'pk', 'name', 'device', 'label', 'type', 'color', 'positions', 'assignments', 'description',
)
default_columns = ('pk', 'name', 'device', 'label', 'type', 'color', 'description')
class DeviceRearPortTable(RearPortTable):
@@ -832,11 +836,11 @@ class DeviceRearPortTable(RearPortTable):
class Meta(CableTerminationTable.Meta, DeviceComponentTable.Meta):
model = models.RearPort
fields = (
'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'positions', 'description', 'mark_connected',
'cable', 'cable_color', 'link_peer', 'tags', 'actions',
'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'color', 'positions', 'assignments',
'description', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'tags', 'actions',
)
default_columns = (
'pk', 'name', 'label', 'type', 'positions', 'description', 'cable', 'link_peer',
'pk', 'name', 'label', 'type', 'positions', 'assignments', 'description', 'cable', 'link_peer',
)

View File

@@ -249,12 +249,13 @@ class InterfaceTemplateTable(ComponentTemplateTable):
class FrontPortTemplateTable(ComponentTemplateTable):
rear_port_position = tables.Column(
verbose_name=_('Position')
)
color = columns.ColorColumn(
verbose_name=_('Color'),
)
assignments = columns.ManyToManyColumn(
verbose_name=_('Assignments'),
transform=lambda obj: f'{obj.rear_port}:{obj.rear_port_position}'
)
actions = columns.ActionsColumn(
actions=('edit', 'delete'),
extra_buttons=MODULAR_COMPONENT_TEMPLATE_BUTTONS
@@ -262,7 +263,7 @@ class FrontPortTemplateTable(ComponentTemplateTable):
class Meta(ComponentTemplateTable.Meta):
model = models.FrontPortTemplate
fields = ('pk', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'description', 'actions')
fields = ('pk', 'name', 'label', 'type', 'color', 'positions', 'assignments', 'description', 'actions')
empty_text = "None"
@@ -270,6 +271,10 @@ class RearPortTemplateTable(ComponentTemplateTable):
color = columns.ColorColumn(
verbose_name=_('Color'),
)
assignments = columns.ManyToManyColumn(
verbose_name=_('Assignments'),
transform=lambda obj: f'{obj.front_port}:{obj.front_port_position}'
)
actions = columns.ActionsColumn(
actions=('edit', 'delete'),
extra_buttons=MODULAR_COMPONENT_TEMPLATE_BUTTONS
@@ -277,7 +282,7 @@ class RearPortTemplateTable(ComponentTemplateTable):
class Meta(ComponentTemplateTable.Meta):
model = models.RearPortTemplate
fields = ('pk', 'name', 'label', 'type', 'color', 'positions', 'description', 'actions')
fields = ('pk', 'name', 'label', 'type', 'color', 'positions', 'assignments', 'description', 'actions')
empty_text = "None"