From 4c76196d8ded74e8af733c66bfd3124b6d152374 Mon Sep 17 00:00:00 2001 From: blackphantom39 Date: Wed, 24 Sep 2025 16:20:26 +0200 Subject: [PATCH 1/3] Add color to PowerOutletTemplate --- .../api/serializers_/devicetype_components.py | 2 +- netbox/dcim/filtersets.py | 2 +- netbox/dcim/forms/bulk_edit.py | 4 ++++ netbox/dcim/forms/model_forms.py | 4 ++-- netbox/dcim/graphql/types.py | 1 + .../0216_poweroutlettemplate_color.py | 17 +++++++++++++++++ .../dcim/models/device_component_templates.py | 6 ++++++ netbox/dcim/tables/devicetypes.py | 2 +- netbox/dcim/tests/test_filtersets.py | 7 +++++++ 9 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 netbox/dcim/migrations/0216_poweroutlettemplate_color.py diff --git a/netbox/dcim/api/serializers_/devicetype_components.py b/netbox/dcim/api/serializers_/devicetype_components.py index 8d4403d2d9..5f2d4b36aa 100644 --- a/netbox/dcim/api/serializers_/devicetype_components.py +++ b/netbox/dcim/api/serializers_/devicetype_components.py @@ -155,7 +155,7 @@ class Meta: model = PowerOutletTemplate fields = [ 'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', - 'power_port', 'feed_leg', 'description', 'created', 'last_updated', + 'color', 'power_port', 'feed_leg', 'description', 'created', 'last_updated', ] brief_fields = ('id', 'url', 'display', 'name', 'description') diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index 37a0d99a2c..f9dd771cdf 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -842,7 +842,7 @@ class PowerOutletTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceType class Meta: model = PowerOutletTemplate - fields = ('id', 'name', 'label', 'type', 'feed_leg', 'description') + fields = ('id', 'name', 'label', 'type', 'color', 'feed_leg', 'description') class InterfaceTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet): diff --git a/netbox/dcim/forms/bulk_edit.py b/netbox/dcim/forms/bulk_edit.py index 0f55506662..653e342498 100644 --- a/netbox/dcim/forms/bulk_edit.py +++ b/netbox/dcim/forms/bulk_edit.py @@ -1163,6 +1163,10 @@ class PowerOutletTemplateBulkEditForm(ComponentTemplateBulkEditForm): choices=add_blank_choice(PowerOutletTypeChoices), required=False ) + color = ColorField( + label=_('Color'), + required=False + ) power_port = forms.ModelChoiceField( label=_('Power port'), queryset=PowerPortTemplate.objects.all(), diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index 32ea2d2636..7501dac0c8 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -1092,14 +1092,14 @@ class PowerOutletTemplateForm(ModularComponentTemplateForm): FieldSet('device_type', name=_('Device Type')), FieldSet('module_type', name=_('Module Type')), ), - 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', + 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'description', ), ) class Meta: model = PowerOutletTemplate fields = [ - 'device_type', 'module_type', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', + 'device_type', 'module_type', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'description', ] diff --git a/netbox/dcim/graphql/types.py b/netbox/dcim/graphql/types.py index 0cd5e8fd11..d29d5fa05a 100644 --- a/netbox/dcim/graphql/types.py +++ b/netbox/dcim/graphql/types.py @@ -673,6 +673,7 @@ class PowerOutletType(ModularComponentType, CabledObjectMixin, PathEndpointMixin ) class PowerOutletTemplateType(ModularComponentTemplateType): power_port: Annotated["PowerPortTemplateType", strawberry.lazy('dcim.graphql.types')] | None + color: str @strawberry_django.type( diff --git a/netbox/dcim/migrations/0216_poweroutlettemplate_color.py b/netbox/dcim/migrations/0216_poweroutlettemplate_color.py new file mode 100644 index 0000000000..7e57bbe225 --- /dev/null +++ b/netbox/dcim/migrations/0216_poweroutlettemplate_color.py @@ -0,0 +1,17 @@ +import utilities.fields +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0215_rackreservation_status'), + ] + + operations = [ + migrations.AddField( + model_name='poweroutlettemplate', + name='color', + field=utilities.fields.ColorField(blank=True, max_length=6), + ), + ] \ No newline at end of file diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index e0b05b388a..04c02f84fe 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -338,6 +338,10 @@ class PowerOutletTemplate(ModularComponentTemplateModel): blank=True, null=True ) + color = ColorField( + verbose_name=_('color'), + blank=True + ) power_port = models.ForeignKey( to='dcim.PowerPortTemplate', on_delete=models.SET_NULL, @@ -388,6 +392,7 @@ def instantiate(self, **kwargs): name=self.resolve_name(kwargs.get('module')), label=self.resolve_label(kwargs.get('module')), type=self.type, + color=self.color, power_port=power_port, feed_leg=self.feed_leg, **kwargs @@ -398,6 +403,7 @@ def to_yaml(self): return { 'name': self.name, 'type': self.type, + 'color': self.color, 'power_port': self.power_port.name if self.power_port else None, 'feed_leg': self.feed_leg, 'label': self.label, diff --git a/netbox/dcim/tables/devicetypes.py b/netbox/dcim/tables/devicetypes.py index 91f9f3b479..e4a2cf6f72 100644 --- a/netbox/dcim/tables/devicetypes.py +++ b/netbox/dcim/tables/devicetypes.py @@ -218,7 +218,7 @@ class PowerOutletTemplateTable(ComponentTemplateTable): class Meta(ComponentTemplateTable.Meta): model = models.PowerOutletTemplate - fields = ('pk', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', 'actions') + fields = ('pk', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'description', 'actions') empty_text = "None" diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py index c05d07ab03..4edd682155 100644 --- a/netbox/dcim/tests/test_filtersets.py +++ b/netbox/dcim/tests/test_filtersets.py @@ -1919,18 +1919,21 @@ def setUpTestData(cls): device_type=device_types[0], name='Power Outlet 1', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A, + color=ColorChoices.COLOR_RED, description='foobar1' ), PowerOutletTemplate( device_type=device_types[1], name='Power Outlet 2', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_B, + color=ColorChoices.COLOR_GREEN, description='foobar2' ), PowerOutletTemplate( device_type=device_types[2], name='Power Outlet 3', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_C, + color=ColorChoices.COLOR_BLUE, description='foobar3' ), )) @@ -1942,6 +1945,10 @@ def test_name(self): def test_feed_leg(self): params = {'feed_leg': [PowerOutletFeedLegChoices.FEED_LEG_A, PowerOutletFeedLegChoices.FEED_LEG_B]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_color(self): + params = {'color': [ColorChoices.COLOR_RED, ColorChoices.COLOR_GREEN]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) class InterfaceTemplateTestCase(TestCase, DeviceComponentTemplateFilterSetTests, ChangeLoggedFilterSetTests): From 1e34b7c16dda3d1ff98be285d6b2a9c3884604bc Mon Sep 17 00:00:00 2001 From: blackphantom39 Date: Wed, 8 Oct 2025 15:56:58 +0200 Subject: [PATCH 2/3] fix: pep8 compliance --- netbox/dcim/migrations/0216_poweroutlettemplate_color.py | 2 +- netbox/dcim/tests/test_filtersets.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/dcim/migrations/0216_poweroutlettemplate_color.py b/netbox/dcim/migrations/0216_poweroutlettemplate_color.py index 7e57bbe225..f0c0762e92 100644 --- a/netbox/dcim/migrations/0216_poweroutlettemplate_color.py +++ b/netbox/dcim/migrations/0216_poweroutlettemplate_color.py @@ -14,4 +14,4 @@ class Migration(migrations.Migration): name='color', field=utilities.fields.ColorField(blank=True, max_length=6), ), - ] \ No newline at end of file + ] diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py index 4edd682155..3ab34361ec 100644 --- a/netbox/dcim/tests/test_filtersets.py +++ b/netbox/dcim/tests/test_filtersets.py @@ -1945,7 +1945,7 @@ def test_name(self): def test_feed_leg(self): params = {'feed_leg': [PowerOutletFeedLegChoices.FEED_LEG_A, PowerOutletFeedLegChoices.FEED_LEG_B]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) - + def test_color(self): params = {'color': [ColorChoices.COLOR_RED, ColorChoices.COLOR_GREEN]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) From 068fc22b99d2f66ac28d69951de07c63c30bca4a Mon Sep 17 00:00:00 2001 From: blackphantom39 Date: Fri, 10 Oct 2025 11:11:10 +0200 Subject: [PATCH 3/3] fix(tables): correctly display color instead of hex value --- netbox/dcim/tables/devicetypes.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/netbox/dcim/tables/devicetypes.py b/netbox/dcim/tables/devicetypes.py index e4a2cf6f72..713d3f886a 100644 --- a/netbox/dcim/tables/devicetypes.py +++ b/netbox/dcim/tables/devicetypes.py @@ -211,6 +211,9 @@ class Meta(ComponentTemplateTable.Meta): class PowerOutletTemplateTable(ComponentTemplateTable): + color = columns.ColorColumn( + verbose_name=_('Color'), + ) actions = columns.ActionsColumn( actions=('edit', 'delete'), extra_buttons=MODULAR_COMPONENT_TEMPLATE_BUTTONS