This commit is contained in:
Jeremy Stretch 2025-10-30 10:53:46 -04:00
parent 3890043b06
commit d4783b7fbd
7 changed files with 80 additions and 86 deletions

View File

@ -1,26 +0,0 @@
from django.utils.translation import gettext_lazy as _
from netbox.templates.components import (
GPSCoordinatesAttr, NestedObjectAttr, ObjectAttr, ObjectDetailsPanel, TemplatedAttr, TextAttr,
)
class DevicePanel(ObjectDetailsPanel):
region = NestedObjectAttr('site.region', linkify=True)
site = ObjectAttr('site', linkify=True, grouped_by='group')
location = NestedObjectAttr('location', linkify=True)
rack = TemplatedAttr('rack', template_name='dcim/device/attrs/rack.html')
virtual_chassis = NestedObjectAttr('virtual_chassis', linkify=True)
parent_device = TemplatedAttr(
'parent_bay',
template_name='dcim/device/attrs/parent_device.html',
label=_('Parent Device'),
)
gps_coordinates = GPSCoordinatesAttr()
tenant = ObjectAttr('tenant', linkify=True, grouped_by='group')
device_type = ObjectAttr('device_type', linkify=True, grouped_by='manufacturer')
description = TextAttr('description')
airflow = TextAttr('get_airflow_display')
serial = TextAttr('serial', style='font-monospace')
asset_tag = TextAttr('asset_tag', style='font-monospace')
config_template = ObjectAttr('config_template', linkify=True)

25
netbox/dcim/ui/panels.py Normal file
View File

@ -0,0 +1,25 @@
from django.utils.translation import gettext_lazy as _
from netbox.ui import attrs
from netbox.ui.components import ObjectPanel
class DevicePanel(ObjectPanel):
region = attrs.NestedObjectAttr('site.region', linkify=True)
site = attrs.ObjectAttr('site', linkify=True, grouped_by='group')
location = attrs.NestedObjectAttr('location', linkify=True)
rack = attrs.TemplatedAttr('rack', template_name='dcim/device/attrs/rack.html')
virtual_chassis = attrs.NestedObjectAttr('virtual_chassis', linkify=True)
parent_device = attrs.TemplatedAttr(
'parent_bay',
template_name='dcim/device/attrs/parent_device.html',
label=_('Parent Device'),
)
gps_coordinates = attrs.GPSCoordinatesAttr()
tenant = attrs.ObjectAttr('tenant', linkify=True, grouped_by='group')
device_type = attrs.ObjectAttr('device_type', linkify=True, grouped_by='manufacturer')
description = attrs.TextAttr('description')
airflow = attrs.TextAttr('get_airflow_display')
serial = attrs.TextAttr('serial', style='font-monospace')
asset_tag = attrs.TextAttr('asset_tag', style='font-monospace')
config_template = attrs.ObjectAttr('config_template', linkify=True)

View File

@ -12,7 +12,7 @@ from django.utils.translation import gettext_lazy as _
from django.views.generic import View from django.views.generic import View
from circuits.models import Circuit, CircuitTermination from circuits.models import Circuit, CircuitTermination
from dcim.template_components.object_panels import DevicePanel from dcim.ui.panels import DevicePanel
from extras.views import ObjectConfigContextView, ObjectRenderConfigView from extras.views import ObjectConfigContextView, ObjectRenderConfigView
from ipam.models import ASN, IPAddress, Prefix, VLANGroup, VLAN from ipam.models import ASN, IPAddress, Prefix, VLANGroup, VLAN
from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable

View File

@ -1,13 +1,9 @@
from abc import ABC, ABCMeta, abstractmethod
from functools import cached_property
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.html import escape from django.utils.html import escape
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from netbox.config import get_config from netbox.config import get_config
from utilities.string import title
# #
@ -56,10 +52,6 @@ class ObjectAttr(Attr):
self.linkify = linkify self.linkify = linkify
self.grouped_by = grouped_by self.grouped_by = grouped_by
# Derive label from related object if not explicitly set
if self.label is None:
self.label = title(self.accessor)
def render(self, obj): def render(self, obj):
value = self._resolve_attr(obj, self.accessor) value = self._resolve_attr(obj, self.accessor)
if value is None: if value is None:
@ -132,54 +124,3 @@ class TemplatedAttr(Attr):
'value': self._resolve_attr(obj, self.accessor), 'value': self._resolve_attr(obj, self.accessor),
} }
) )
#
# Components
#
class Component(ABC):
@abstractmethod
def render(self):
pass
def __str__(self):
return self.render()
class ObjectDetailsPanelMeta(ABCMeta):
def __new__(mcls, name, bases, attrs):
# Collect all declared attributes
attrs['_attrs'] = {}
for key, val in list(attrs.items()):
if isinstance(val, Attr):
attrs['_attrs'][key] = val
return super().__new__(mcls, name, bases, attrs)
class ObjectDetailsPanel(Component, metaclass=ObjectDetailsPanelMeta):
template_name = 'components/object_details_panel.html'
def __init__(self, obj, title=None):
self.object = obj
self.title = title or obj._meta.verbose_name
@cached_property
def attributes(self):
return [
{
'label': attr.label or title(name),
'value': attr.render(self.object),
} for name, attr in self._attrs.items()
]
def render(self):
return render_to_string(self.template_name, {
'title': self.title,
'attrs': self.attributes,
})
def __str__(self):
return self.render()

View File

@ -0,0 +1,54 @@
from abc import ABC, ABCMeta, abstractmethod
from functools import cached_property
from django.template.loader import render_to_string
from netbox.ui.attrs import Attr
from utilities.string import title
class Component(ABC):
@abstractmethod
def render(self):
pass
def __str__(self):
return self.render()
class ObjectDetailsPanelMeta(ABCMeta):
def __new__(mcls, name, bases, attrs):
# Collect all declared attributes
attrs['_attrs'] = {}
for key, val in list(attrs.items()):
if isinstance(val, Attr):
attrs['_attrs'][key] = val
return super().__new__(mcls, name, bases, attrs)
class ObjectPanel(Component, metaclass=ObjectDetailsPanelMeta):
template_name = 'components/object_details_panel.html'
def __init__(self, obj, title=None):
self.object = obj
self.title = title or obj._meta.verbose_name
@cached_property
def attributes(self):
return [
{
'label': attr.label or title(name),
'value': attr.render(self.object),
} for name, attr in self._attrs.items()
]
def render(self):
return render_to_string(self.template_name, {
'title': self.title,
'attrs': self.attributes,
})
def __str__(self):
return self.render()