Add device_status to common superclasses for Device Components, and refactor ChoiceFieldColumn to support a "color" callable allowing get_FOO_color behavior to be overridden

This commit is contained in:
Brian Tiemann 2024-08-26 21:21:55 -04:00
parent 698155e66e
commit cd7c7f4143
5 changed files with 73 additions and 38 deletions

View File

@ -1411,6 +1411,10 @@ class DeviceComponentFilterSet(django_filters.FilterSet):
to_field_name='name',
label=_('Virtual Chassis'),
)
device_status = django_filters.MultipleChoiceFilter(
choices=DeviceStatusChoices,
field_name='device__status',
)
def search(self, queryset, name, value):
if not value.strip():
@ -1479,7 +1483,7 @@ class ConsolePortFilterSet(
class Meta:
model = ConsolePort
fields = ('id', 'name', 'label', 'speed', 'description', 'mark_connected', 'cable_end')
fields = ('id', 'name', 'label', 'speed', 'description', 'mark_connected', 'cable_end', 'device_status')
class ConsoleServerPortFilterSet(
@ -1495,7 +1499,7 @@ class ConsoleServerPortFilterSet(
class Meta:
model = ConsoleServerPort
fields = ('id', 'name', 'label', 'speed', 'description', 'mark_connected', 'cable_end')
fields = ('id', 'name', 'label', 'speed', 'description', 'mark_connected', 'cable_end', 'device_status')
class PowerPortFilterSet(
@ -1513,6 +1517,7 @@ class PowerPortFilterSet(
model = PowerPort
fields = (
'id', 'name', 'label', 'maximum_draw', 'allocated_draw', 'description', 'mark_connected', 'cable_end',
'device_status',
)
@ -1538,7 +1543,7 @@ class PowerOutletFilterSet(
class Meta:
model = PowerOutlet
fields = (
'id', 'name', 'label', 'feed_leg', 'description', 'mark_connected', 'cable_end',
'id', 'name', 'label', 'feed_leg', 'description', 'mark_connected', 'cable_end', 'device_status',
)
@ -1683,7 +1688,7 @@ class InterfaceFilterSet(
fields = (
'id', 'name', 'label', 'type', 'enabled', 'mtu', 'mgmt_only', 'poe_mode', 'poe_type', 'mode', 'rf_role',
'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'description', 'mark_connected',
'cable_id', 'cable_end',
'cable_id', 'cable_end', 'device_status',
)
def filter_virtual_chassis_member(self, queryset, name, value):
@ -1721,6 +1726,7 @@ class FrontPortFilterSet(
model = FrontPort
fields = (
'id', 'name', 'label', 'type', 'color', 'rear_port_position', 'description', 'mark_connected', 'cable_end',
'device_status',
)
@ -1738,6 +1744,7 @@ class RearPortFilterSet(
model = RearPort
fields = (
'id', 'name', 'label', 'type', 'color', 'positions', 'description', 'mark_connected', 'cable_end',
'device_status',
)
@ -1750,7 +1757,7 @@ class ModuleBayFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet):
class Meta:
model = ModuleBay
fields = ('id', 'name', 'label', 'position', 'description')
fields = ('id', 'name', 'label', 'position', 'description', 'device_status',)
class DeviceBayFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet):
@ -1767,7 +1774,7 @@ class DeviceBayFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet):
class Meta:
model = DeviceBay
fields = ('id', 'name', 'label', 'description')
fields = ('id', 'name', 'label', 'description', 'device_status')
class InventoryItemFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet):
@ -1795,10 +1802,6 @@ class InventoryItemFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet):
to_field_name='slug',
label=_('Role (slug)'),
)
device_status = django_filters.MultipleChoiceFilter(
choices=DeviceStatusChoices,
field_name='device__status',
)
component_type = ContentTypeFilter()
component_id = MultiValueNumberFilter()
serial = MultiValueCharFilter(

View File

@ -129,6 +129,11 @@ class DeviceComponentFilterForm(NetBoxModelFilterSetForm):
},
label=_('Device')
)
device_status = forms.MultipleChoiceField(
choices=DeviceStatusChoices,
required=False,
label=_('Device Status'),
)
class RegionFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
@ -1173,7 +1178,9 @@ class ConsolePortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', 'type', 'speed', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id', name=_('Device')),
FieldSet(
'device_type_id', 'device_role_id', 'device_id', 'device_status', 'virtual_chassis_id', name=_('Device')
),
FieldSet('cabled', 'connected', 'occupied', name=_('Connection')),
)
type = forms.MultipleChoiceField(
@ -1195,7 +1202,10 @@ class ConsoleServerPortFilterForm(PathEndpointFilterForm, DeviceComponentFilterF
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', 'type', 'speed', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id', name=_('Device')),
FieldSet(
'device_type_id', 'device_role_id', 'device_id', 'device_status', 'virtual_chassis_id',
name=_('Device')
),
FieldSet('cabled', 'connected', 'occupied', name=_('Connection')),
)
type = forms.MultipleChoiceField(
@ -1217,7 +1227,9 @@ class PowerPortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', 'type', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id', name=_('Device')),
FieldSet(
'device_type_id', 'device_role_id', 'device_id', 'device_status', 'virtual_chassis_id', name=_('Device')
),
FieldSet('cabled', 'connected', 'occupied', name=_('Connection')),
)
type = forms.MultipleChoiceField(
@ -1234,7 +1246,10 @@ class PowerOutletFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', 'type', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id', name=_('Device')),
FieldSet(
'device_type_id', 'device_role_id', 'device_id', 'device_status', 'virtual_chassis_id',
name=_('Device')
),
FieldSet('cabled', 'connected', 'occupied', name=_('Connection')),
)
type = forms.MultipleChoiceField(
@ -1254,7 +1269,10 @@ class InterfaceFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
FieldSet('poe_mode', 'poe_type', name=_('PoE')),
FieldSet('rf_role', 'rf_channel', 'rf_channel_width', 'tx_power', name=_('Wireless')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id', 'vdc_id', name=_('Device')),
FieldSet(
'device_type_id', 'device_role_id', 'device_id', 'device_status', 'virtual_chassis_id', 'vdc_id',
name=_('Device')
),
FieldSet('cabled', 'connected', 'occupied', name=_('Connection')),
)
selector_fields = ('filter_id', 'q', 'device_id')
@ -1362,7 +1380,9 @@ class FrontPortFilterForm(CabledFilterForm, DeviceComponentFilterForm):
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', 'type', 'color', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id', name=_('Device')),
FieldSet(
'device_type_id', 'device_role_id', 'device_id', 'device_status', 'virtual_chassis_id', name=_('Device')
),
FieldSet('cabled', 'occupied', name=_('Cable')),
)
model = FrontPort
@ -1384,7 +1404,10 @@ class RearPortFilterForm(CabledFilterForm, DeviceComponentFilterForm):
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', 'type', 'color', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id', name=_('Device')),
FieldSet(
'device_type_id', 'device_role_id', 'device_id', 'device_status', 'virtual_chassis_id',
name=_('Device')
),
FieldSet('cabled', 'occupied', name=_('Cable')),
)
type = forms.MultipleChoiceField(
@ -1405,7 +1428,10 @@ class ModuleBayFilterForm(DeviceComponentFilterForm):
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', 'position', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id', name=_('Device')),
FieldSet(
'device_type_id', 'device_role_id', 'device_id', 'device_status', 'virtual_chassis_id',
name=_('Device')
),
)
tag = TagFilterField(model)
position = forms.CharField(
@ -1420,7 +1446,10 @@ class DeviceBayFilterForm(DeviceComponentFilterForm):
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet('device_type_id', 'device_role_id', 'device_id', 'virtual_chassis_id', name=_('Device')),
FieldSet(
'device_type_id', 'device_role_id', 'device_id', 'device_status', 'virtual_chassis_id',
name=_('Device')
),
)
tag = TagFilterField(model)
@ -1434,7 +1463,10 @@ class InventoryItemFilterForm(DeviceComponentFilterForm):
name=_('Attributes')
),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet('device_type_id', 'device_role_id', 'device_id', 'device_status', 'virtual_chassis_id', name=_('Device')),
FieldSet(
'device_type_id', 'device_role_id', 'device_id', 'device_status', 'virtual_chassis_id',
name=_('Device')
),
)
role_id = DynamicModelMultipleChoiceField(
queryset=InventoryItemRole.objects.all(),
@ -1461,11 +1493,6 @@ class InventoryItemFilterForm(DeviceComponentFilterForm):
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
device_status = forms.MultipleChoiceField(
choices=DeviceStatusChoices,
required=False,
label=_('Device Status'),
)
tag = TagFilterField(model)

View File

@ -1265,9 +1265,6 @@ class InventoryItem(MPTTModel, ComponentModel, TrackingModelMixin):
def get_absolute_url(self):
return reverse('dcim:inventoryitem', kwargs={'pk': self.pk})
def get_device_status_color(self):
return self.device.get_status_color()
def clean(self):
super().clean()

View File

@ -290,6 +290,11 @@ class DeviceComponentTable(NetBoxTable):
linkify=True,
order_by=('_name',)
)
device_status = columns.ChoiceFieldColumn(
accessor=tables.A('device__status'),
verbose_name=_('Device Status'),
color=lambda x: x.device.get_status_color(),
)
class Meta(NetBoxTable.Meta):
order_by = ('device', 'name')
@ -927,10 +932,6 @@ class InventoryItemTable(DeviceComponentTable):
tags = columns.TagColumn(
url_name='dcim:inventoryitem_list'
)
device_status = columns.ChoiceFieldColumn(
accessor=tables.A('device__status'),
verbose_name=_('Device Status'),
)
cable = None # Override DeviceComponentTable
class Meta(NetBoxTable.Meta):

View File

@ -330,15 +330,22 @@ class ActionsColumn(tables.Column):
class ChoiceFieldColumn(tables.Column):
"""
Render a model's static ChoiceField with its value from `get_FOO_display()` as a colored badge. Background color is
set by the instance's get_FOO_color() method, if defined.
set by the instance's get_FOO_color() method, if defined, or can be overridden by a "color" callable.
"""
DEFAULT_BG_COLOR = 'secondary'
def __init__(self, *args, color=None, **kwargs):
super().__init__(*args, **kwargs)
self.color = color
def render(self, record, bound_column, value):
if value in self.empty_values:
return self.default
# Determine the background color to use (try calling object.get_FOO_color())
# Determine the background color to use (use "color" callable if given, else try calling object.get_FOO_color())
if self.color:
bg_color = self.color(record)
else:
try:
bg_color = getattr(record, f'get_{bound_column.name}_color')() or self.DEFAULT_BG_COLOR
except AttributeError: