mirror of
https://github.com/netbox-community/netbox.git
synced 2025-12-14 12:29:35 -06:00
Enable panel inheritance; add location panel
This commit is contained in:
parent
83de784196
commit
2a629d6f74
@ -1,10 +1,16 @@
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from netbox.ui import attrs
|
||||
from netbox.ui.components import ObjectPanel
|
||||
from netbox.ui import attrs, components
|
||||
|
||||
|
||||
class DevicePanel(ObjectPanel):
|
||||
class LocationPanel(components.NestedGroupObjectPanel):
|
||||
site = attrs.ObjectAttr('site', label=_('Site'), linkify=True, grouped_by='group')
|
||||
status = attrs.ChoiceAttr('status', label=_('Status'))
|
||||
tenant = attrs.ObjectAttr('tenant', label=_('Tenant'), linkify=True, grouped_by='group')
|
||||
facility = attrs.TextAttr('facility', label=_('Facility'))
|
||||
|
||||
|
||||
class DevicePanel(components.ObjectPanel):
|
||||
region = attrs.NestedObjectAttr('site.region', label=_('Region'), linkify=True)
|
||||
site = attrs.ObjectAttr('site', label=_('Site'), linkify=True, grouped_by='group')
|
||||
location = attrs.NestedObjectAttr('location', label=_('Location'), linkify=True)
|
||||
@ -25,7 +31,7 @@ class DevicePanel(ObjectPanel):
|
||||
config_template = attrs.ObjectAttr('config_template', label=_('Config template'), linkify=True)
|
||||
|
||||
|
||||
class DeviceManagementPanel(ObjectPanel):
|
||||
class DeviceManagementPanel(components.ObjectPanel):
|
||||
status = attrs.ChoiceAttr('status', label=_('Status'))
|
||||
role = attrs.NestedObjectAttr('role', label=_('Role'), linkify=True, max_depth=3)
|
||||
platform = attrs.NestedObjectAttr('platform', label=_('Platform'), linkify=True, max_depth=3)
|
||||
@ -46,7 +52,7 @@ class DeviceManagementPanel(ObjectPanel):
|
||||
)
|
||||
|
||||
|
||||
class SitePanel(ObjectPanel):
|
||||
class SitePanel(components.ObjectPanel):
|
||||
region = attrs.NestedObjectAttr('region', label=_('Region'), linkify=True)
|
||||
group = attrs.NestedObjectAttr('group', label=_('Group'), linkify=True)
|
||||
status = attrs.ChoiceAttr('status', label=_('Status'))
|
||||
|
||||
@ -571,6 +571,7 @@ class LocationView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
locations = instance.get_descendants(include_self=True)
|
||||
location_content_type = ContentType.objects.get_for_model(instance)
|
||||
return {
|
||||
'location_panel': panels.LocationPanel(instance, _('Location')),
|
||||
'related_models': self.get_related_models(
|
||||
request,
|
||||
locations,
|
||||
|
||||
@ -21,13 +21,29 @@ class Component(ABC):
|
||||
|
||||
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)
|
||||
def __new__(mcls, name, bases, namespace, **kwargs):
|
||||
declared = {}
|
||||
|
||||
# Walk MRO parents (excluding `object`) for declared attributes
|
||||
for base in reversed([b for b in bases if hasattr(b, "_attrs")]):
|
||||
for key, attr in getattr(base, '_attrs', {}).items():
|
||||
if key not in declared:
|
||||
declared[key] = attr
|
||||
|
||||
# Add local declarations in the order they appear in the class body
|
||||
for key, attr in namespace.items():
|
||||
if isinstance(attr, Attr):
|
||||
declared[key] = attr
|
||||
|
||||
namespace['_attrs'] = declared
|
||||
|
||||
# Remove Attrs from the class namespace to keep things tidy
|
||||
local_items = [key for key, attr in namespace.items() if isinstance(attr, Attr)]
|
||||
for key in local_items:
|
||||
namespace.pop(key)
|
||||
|
||||
cls = super().__new__(mcls, name, bases, namespace, **kwargs)
|
||||
return cls
|
||||
|
||||
|
||||
class ObjectPanel(Component, metaclass=ObjectDetailsPanelMeta):
|
||||
@ -56,7 +72,7 @@ class ObjectPanel(Component, metaclass=ObjectDetailsPanelMeta):
|
||||
return self.render()
|
||||
|
||||
|
||||
class NestedGroupObjectPanel(ObjectPanel):
|
||||
class NestedGroupObjectPanel(ObjectPanel, metaclass=ObjectDetailsPanelMeta):
|
||||
name = attrs.TextAttr('name', label=_('Name'))
|
||||
description = attrs.TextAttr('description', label=_('Description'))
|
||||
parent = attrs.NestedObjectAttr('parent', label=_('Parent'), linkify=True)
|
||||
|
||||
@ -22,44 +22,7 @@
|
||||
{% block content %}
|
||||
<div class="row mb-3">
|
||||
<div class="col col-12 col-md-6">
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Location" %}</h2>
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Name" %}</th>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Description" %}</th>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Site" %}</th>
|
||||
<td>{{ object.site|linkify }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Parent" %}</th>
|
||||
<td>{{ object.parent|linkify|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Status" %}</th>
|
||||
<td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Tenant" %}</th>
|
||||
<td>
|
||||
{% if object.tenant.group %}
|
||||
{{ object.tenant.group|linkify }} /
|
||||
{% endif %}
|
||||
{{ object.tenant|linkify|placeholder }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Facility" %}</th>
|
||||
<td>{{ object.facility|placeholder }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{{ location_panel }}
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
{% include 'inc/panels/custom_fields.html' %}
|
||||
{% include 'inc/panels/comments.html' %}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user