Просмотр исходного кода

Enable panel inheritance; add location panel

Jeremy Stretch 3 месяцев назад
Родитель
Сommit
2a629d6f74
4 измененных файлов с 37 добавлено и 51 удалено
  1. 11 5
      netbox/dcim/ui/panels.py
  2. 1 0
      netbox/dcim/views.py
  3. 24 8
      netbox/netbox/ui/components.py
  4. 1 38
      netbox/templates/dcim/location.html

+ 11 - 5
netbox/dcim/ui/panels.py

@@ -1,10 +1,16 @@
 from django.utils.translation import gettext_lazy as _
 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)
     region = attrs.NestedObjectAttr('site.region', label=_('Region'), linkify=True)
     site = attrs.ObjectAttr('site', label=_('Site'), linkify=True, grouped_by='group')
     site = attrs.ObjectAttr('site', label=_('Site'), linkify=True, grouped_by='group')
     location = attrs.NestedObjectAttr('location', label=_('Location'), linkify=True)
     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)
     config_template = attrs.ObjectAttr('config_template', label=_('Config template'), linkify=True)
 
 
 
 
-class DeviceManagementPanel(ObjectPanel):
+class DeviceManagementPanel(components.ObjectPanel):
     status = attrs.ChoiceAttr('status', label=_('Status'))
     status = attrs.ChoiceAttr('status', label=_('Status'))
     role = attrs.NestedObjectAttr('role', label=_('Role'), linkify=True, max_depth=3)
     role = attrs.NestedObjectAttr('role', label=_('Role'), linkify=True, max_depth=3)
     platform = attrs.NestedObjectAttr('platform', label=_('Platform'), 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)
     region = attrs.NestedObjectAttr('region', label=_('Region'), linkify=True)
     group = attrs.NestedObjectAttr('group', label=_('Group'), linkify=True)
     group = attrs.NestedObjectAttr('group', label=_('Group'), linkify=True)
     status = attrs.ChoiceAttr('status', label=_('Status'))
     status = attrs.ChoiceAttr('status', label=_('Status'))

+ 1 - 0
netbox/dcim/views.py

@@ -571,6 +571,7 @@ class LocationView(GetRelatedModelsMixin, generic.ObjectView):
         locations = instance.get_descendants(include_self=True)
         locations = instance.get_descendants(include_self=True)
         location_content_type = ContentType.objects.get_for_model(instance)
         location_content_type = ContentType.objects.get_for_model(instance)
         return {
         return {
+            'location_panel': panels.LocationPanel(instance, _('Location')),
             'related_models': self.get_related_models(
             'related_models': self.get_related_models(
                 request,
                 request,
                 locations,
                 locations,

+ 24 - 8
netbox/netbox/ui/components.py

@@ -21,13 +21,29 @@ class Component(ABC):
 
 
 class ObjectDetailsPanelMeta(ABCMeta):
 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):
 class ObjectPanel(Component, metaclass=ObjectDetailsPanelMeta):
@@ -56,7 +72,7 @@ class ObjectPanel(Component, metaclass=ObjectDetailsPanelMeta):
         return self.render()
         return self.render()
 
 
 
 
-class NestedGroupObjectPanel(ObjectPanel):
+class NestedGroupObjectPanel(ObjectPanel, metaclass=ObjectDetailsPanelMeta):
     name = attrs.TextAttr('name', label=_('Name'))
     name = attrs.TextAttr('name', label=_('Name'))
     description = attrs.TextAttr('description', label=_('Description'))
     description = attrs.TextAttr('description', label=_('Description'))
     parent = attrs.NestedObjectAttr('parent', label=_('Parent'), linkify=True)
     parent = attrs.NestedObjectAttr('parent', label=_('Parent'), linkify=True)

+ 1 - 38
netbox/templates/dcim/location.html

@@ -22,44 +22,7 @@
 {% block content %}
 {% block content %}
 <div class="row mb-3">
 <div class="row mb-3">
 	<div class="col col-12 col-md-6">
 	<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/tags.html' %}
     {% include 'inc/panels/custom_fields.html' %}
     {% include 'inc/panels/custom_fields.html' %}
     {% include 'inc/panels/comments.html' %}
     {% include 'inc/panels/comments.html' %}