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

Standardize related model display for nested models

jeremystretch 3 лет назад
Родитель
Сommit
91b81d51da

+ 30 - 5
netbox/dcim/views.py

@@ -212,6 +212,18 @@ class RegionListView(generic.ObjectListView):
 class RegionView(generic.ObjectView):
     queryset = Region.objects.all()
 
+    def get_extra_context(self, request, instance):
+        regions = instance.get_descendants(include_self=True)
+        related_models = (
+            (Site.objects.restrict(request.user, 'view').filter(region__in=regions), 'region_id'),
+            (Location.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
+            (Rack.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
+        )
+
+        return {
+            'related_models': related_models,
+        }
+
 
 @register_model_view(Region, 'edit')
 class RegionEditView(generic.ObjectEditView):
@@ -276,6 +288,18 @@ class SiteGroupListView(generic.ObjectListView):
 class SiteGroupView(generic.ObjectView):
     queryset = SiteGroup.objects.all()
 
+    def get_extra_context(self, request, instance):
+        groups = instance.get_descendants(include_self=True)
+        related_models = (
+            (Site.objects.restrict(request.user, 'view').filter(group__in=groups), 'group_id'),
+            (Location.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
+            (Rack.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
+        )
+
+        return {
+            'related_models': related_models,
+        }
+
 
 @register_model_view(SiteGroup, 'edit')
 class SiteGroupEditView(generic.ObjectEditView):
@@ -441,9 +465,11 @@ class LocationView(generic.ObjectView):
     queryset = Location.objects.all()
 
     def get_extra_context(self, request, instance):
-        location_ids = instance.get_descendants(include_self=True).values_list('pk', flat=True)
-        rack_count = Rack.objects.filter(location__in=location_ids).count()
-        device_count = Device.objects.filter(location__in=location_ids).count()
+        locations = instance.get_descendants(include_self=True)
+        related_models = (
+            (Rack.objects.restrict(request.user, 'view').filter(location__in=locations), 'location_id'),
+            (Device.objects.restrict(request.user, 'view').filter(location__in=locations), 'location_id'),
+        )
 
         nonracked_devices = Device.objects.filter(
             location=instance,
@@ -452,8 +478,7 @@ class LocationView(generic.ObjectView):
         ).prefetch_related('device_type__manufacturer', 'parent_bay', 'device_role')
 
         return {
-            'rack_count': rack_count,
-            'device_count': device_count,
+            'related_models': related_models,
             'nonracked_devices': nonracked_devices.order_by('-pk')[:10],
             'total_nonracked_devices_count': nonracked_devices.count(),
         }

+ 2 - 20
netbox/templates/dcim/location.html

@@ -56,33 +56,15 @@
               {{ object.tenant|linkify|placeholder }}
             </td>
           </tr>
-          <tr>
-            <th scope="row">Racks</th>
-            <td class="position-relative">
-              {% if rack_count %}
-                <div class="position-absolute top-50 end-0 translate-middle-y noprint">
-                  <a href="{% url 'dcim:rack_elevation_list' %}?location_id={{ object.pk }}" class="btn btn-sm btn-primary" title="View elevations">
-                    <i class="mdi mdi-server"></i>
-                  </a>
-                </div>
-              {% endif %}
-              <a href="{% url 'dcim:rack_list' %}?location_id={{ object.pk }}">{{ rack_count }}</a>
-            </td>
-          </tr>
-          <tr>
-            <th scope="row">Devices</th>
-            <td>
-              <a href="{% url 'dcim:device_list' %}?location_id={{ object.pk }}">{{ device_count }}</a>
-            </td>
-          </tr>
         </table>
       </div>
     </div>
     {% include 'inc/panels/tags.html' %}
+    {% include 'inc/panels/custom_fields.html' %}
     {% plugin_left_page object %}
   </div>
 	<div class="col col-md-6">
-    {% include 'inc/panels/custom_fields.html' %}
+    {% include 'inc/panels/related_objects.html' %}
     {% include 'inc/panels/contacts.html' %}
     {% include 'dcim/inc/nonracked_devices.html' %}
     {% include 'inc/panels/image_attachments.html' %}

+ 7 - 19
netbox/templates/dcim/region.html

@@ -37,21 +37,21 @@
             <th scope="row">Parent</th>
             <td>{{ object.parent|linkify|placeholder }}</td>
           </tr>
-          <tr>
-            <th scope="row">Sites</th>
-            <td>
-              <a href="{% url 'dcim:site_list' %}?region_id={{ object.pk }}">{{ object.sites.count }}</a>
-            </td>
-          </tr>
         </table>
       </div>
     </div>
     {% include 'inc/panels/tags.html' %}
     {% include 'inc/panels/custom_fields.html' %}
-    {% include 'inc/panels/contacts.html' %}
     {% plugin_left_page object %}
   </div>
 	<div class="col col-md-6">
+    {% include 'inc/panels/related_objects.html' %}
+    {% include 'inc/panels/contacts.html' %}
+    {% plugin_right_page object %}
+	</div>
+</div>
+<div class="row mb-3">
+	<div class="col col-md-12">
     <div class="card">
       <h5 class="card-header">Child Regions</h5>
       <div class="card-body htmx-container table-responsive"
@@ -66,18 +66,6 @@
         </div>
       {% endif %}
     </div>
-    {% plugin_right_page object %}
-	</div>
-</div>
-<div class="row mb-3">
-	<div class="col col-md-12">
-    <div class="card">
-      <h5 class="card-header">Sites</h5>
-      <div class="card-body htmx-container table-responsive"
-        hx-get="{% url 'dcim:site_list' %}?region_id={{ object.pk }}"
-        hx-trigger="load"
-      ></div>
-    </div>
     {% plugin_full_width_page object %}
   </div>
 </div>

+ 6 - 18
netbox/templates/dcim/sitegroup.html

@@ -37,12 +37,6 @@
             <th scope="row">Parent</th>
             <td>{{ object.parent|linkify|placeholder }}</td>
           </tr>
-          <tr>
-            <th scope="row">Sites</th>
-            <td>
-              <a href="{% url 'dcim:site_list' %}?group_id={{ object.pk }}">{{ object.sites.count }}</a>
-            </td>
-          </tr>
         </table>
       </div>
     </div>
@@ -52,6 +46,12 @@
     {% plugin_left_page object %}
   </div>
 	<div class="col col-md-6">
+    {% include 'inc/panels/related_objects.html' %}
+    {% plugin_right_page object %}
+	</div>
+</div>
+<div class="row mb-3">
+	<div class="col col-md-12">
     <div class="card">
       <h5 class="card-header">Child Groups</h5>
       <div class="card-body htmx-container table-responsive"
@@ -66,18 +66,6 @@
         </div>
       {% endif %}
     </div>
-    {% plugin_right_page object %}
-	</div>
-</div>
-<div class="row mb-3">
-	<div class="col col-md-12">
-    <div class="card">
-      <h5 class="card-header">Sites</h5>
-      <div class="card-body htmx-container table-responsive"
-        hx-get="{% url 'dcim:site_list' %}?group_id={{ object.pk }}"
-        hx-trigger="load"
-      ></div>
-    </div>
     {% plugin_full_width_page object %}
   </div>
 </div>

+ 2 - 2
netbox/templates/inc/panels/related_objects.html

@@ -3,9 +3,9 @@
 <div class="card">
   <h5 class="card-header">Related Objects</h5>
   <ul class="list-group list-group-flush">
-    {% for qs in related_models %}
+    {% for qs, filter_param in related_models %}
       {% with viewname=qs.model|viewname:"list" %}
-        <a href="{% url viewname %}?{{ filter_name }}={{ object.pk }}" class="list-group-item list-group-item-action d-flex justify-content-between">
+        <a href="{% url viewname %}?{{ filter_param }}={{ object.pk }}" class="list-group-item list-group-item-action d-flex justify-content-between">
           {{ qs.model|meta:"verbose_name_plural"|bettertitle }}
           {% with count=qs.count %}
             {% if count %}

+ 10 - 22
netbox/templates/tenancy/contactgroup.html

@@ -31,12 +31,6 @@
               <th scope="row">Parent</th>
               <td>{{ object.parent|linkify|placeholder }}</td>
             </tr>
-            <tr>
-              <th scope="row">Contacts</th>
-              <td>
-                <a href="{% url 'tenancy:contact_list' %}?group_id={{ object.pk }}">{{ object.contacts.count }}</a>
-              </td>
-            </tr>
           </table>
         </div>
       </div>
@@ -44,31 +38,25 @@
       {% plugin_left_page object %}
     </div>
     <div class="col col-md-6">
+      {% include 'inc/panels/related_objects.html' %}
       {% include 'inc/panels/custom_fields.html' %}
-      <div class="card">
-        <h5 class="card-header">Child Groups</h5>
-        <div class="card-body htmx-container table-responsive"
-          hx-get="{% url 'tenancy:contactgroup_list' %}?parent_id={{ object.pk }}"
-          hx-trigger="load"
-        ></div>
-        {% if perms.tenancy.add_contactgroup %}
-          <div class="card-footer text-end noprint">
-            <a href="{% url 'tenancy:contactgroup_add' %}?parent={{ object.pk }}" class="btn btn-sm btn-primary">
-              <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Contact Group
-            </a>
-          </div>
-        {% endif %}
-      </div>
       {% plugin_right_page object %}
     </div>
   </div>
   <div class="col col-md-12">
     <div class="card">
-      <h5 class="card-header">Contacts</h5>
+      <h5 class="card-header">Child Groups</h5>
       <div class="card-body htmx-container table-responsive"
-        hx-get="{% url 'tenancy:contact_list' %}?group_id={{ object.pk }}"
+        hx-get="{% url 'tenancy:contactgroup_list' %}?parent_id={{ object.pk }}"
         hx-trigger="load"
       ></div>
+      {% if perms.tenancy.add_contactgroup %}
+        <div class="card-footer text-end noprint">
+          <a href="{% url 'tenancy:contactgroup_add' %}?parent={{ object.pk }}" class="btn btn-sm btn-primary">
+            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Contact Group
+          </a>
+        </div>
+      {% endif %}
     </div>
     {% plugin_full_width_page object %}
   </div>

+ 1 - 1
netbox/templates/tenancy/tenant.html

@@ -34,7 +34,7 @@
       {% plugin_left_page object %}
     </div>
     <div class="col col-md-5">
-      {% include 'inc/panels/related_objects.html' with filter_name='tenant_id' %}
+      {% include 'inc/panels/related_objects.html' %}
       {% plugin_right_page object %}
     </div>
   </div>

+ 6 - 18
netbox/templates/tenancy/tenantgroup.html

@@ -39,12 +39,6 @@
             <th scope="row">Parent</th>
             <td>{{ object.parent|linkify|placeholder }}</td>
           </tr>
-          <tr>
-            <th scope="row">Tenants</th>
-            <td>
-              <a href="{% url 'tenancy:tenant_list' %}?group_id={{ object.pk }}">{{ object.tenants.count }}</a>
-            </td>
-          </tr>
         </table>
       </div>
     </div>
@@ -52,7 +46,13 @@
     {% plugin_left_page object %}
   </div>
 	<div class="col col-md-6">
+    {% include 'inc/panels/related_objects.html' %}
     {% include 'inc/panels/custom_fields.html' %}
+    {% plugin_right_page object %}
+	</div>
+</div>
+<div class="row mb-3">
+	<div class="col col-md-12">
     <div class="card">
       <h5 class="card-header">Child Groups</h5>
       <div class="card-body htmx-container table-responsive"
@@ -67,18 +67,6 @@
         </div>
       {% endif %}
     </div>
-    {% plugin_right_page object %}
-	</div>
-</div>
-<div class="row mb-3">
-	<div class="col col-md-12">
-    <div class="card">
-      <h5 class="card-header">Tenants</h5>
-      <div class="card-body htmx-container table-responsive"
-        hx-get="{% url 'tenancy:tenant_list' %}?group_id={{ object.pk }}"
-        hx-trigger="load"
-      ></div>
-    </div>
     {% plugin_full_width_page object %}
   </div>
 </div>

+ 6 - 18
netbox/templates/wireless/wirelesslangroup.html

@@ -37,12 +37,6 @@
             <th scope="row">Parent</th>
             <td>{{ object.parent|linkify|placeholder }}</td>
           </tr>
-          <tr>
-            <th scope="row">Wireless LANs</th>
-            <td>
-              <a href="{% url 'wireless:wirelesslan_list' %}?group_id={{ object.pk }}">{{ object.wirelesslans.count }}</a>
-            </td>
-          </tr>
         </table>
       </div>
     </div>
@@ -50,7 +44,13 @@
     {% plugin_left_page object %}
   </div>
 	<div class="col col-md-6">
+      {% include 'inc/panels/related_objects.html' %}
     {% include 'inc/panels/custom_fields.html' %}
+    {% plugin_right_page object %}
+	</div>
+</div>
+<div class="row mb-3">
+	<div class="col col-md-12">
     <div class="card">
       <h5 class="card-header">Child Groups</h5>
       <div class="card-body htmx-container table-responsive"
@@ -65,18 +65,6 @@
         </div>
       {% endif %}
     </div>
-    {% plugin_right_page object %}
-	</div>
-</div>
-<div class="row mb-3">
-	<div class="col col-md-12">
-    <div class="card">
-      <h5 class="card-header">Wireless LANs</h5>
-      <div class="card-body htmx-container table-responsive"
-        hx-get="{% url 'wireless:wirelesslan_list' %}?group_id={{ object.pk }}"
-        hx-trigger="load"
-      ></div>
-    </div>
     {% plugin_full_width_page object %}
   </div>
 </div>

+ 40 - 20
netbox/tenancy/views.py

@@ -35,6 +35,16 @@ class TenantGroupListView(generic.ObjectListView):
 class TenantGroupView(generic.ObjectView):
     queryset = TenantGroup.objects.all()
 
+    def get_extra_context(self, request, instance):
+        groups = instance.get_descendants(include_self=True)
+        related_models = (
+            (Tenant.objects.restrict(request.user, 'view').filter(group__in=groups), 'group_id'),
+        )
+
+        return {
+            'related_models': related_models,
+        }
+
 
 @register_model_view(TenantGroup, 'edit')
 class TenantGroupEditView(generic.ObjectEditView):
@@ -95,30 +105,30 @@ class TenantView(generic.ObjectView):
     def get_extra_context(self, request, instance):
         related_models = [
             # DCIM
-            Site.objects.restrict(request.user, 'view').filter(tenant=instance),
-            Rack.objects.restrict(request.user, 'view').filter(tenant=instance),
-            RackReservation.objects.restrict(request.user, 'view').filter(tenant=instance),
-            Location.objects.restrict(request.user, 'view').filter(tenant=instance),
-            Device.objects.restrict(request.user, 'view').filter(tenant=instance),
-            VirtualDeviceContext.objects.restrict(request.user, 'view').filter(tenant=instance),
-            Cable.objects.restrict(request.user, 'view').filter(tenant=instance),
+            (Site.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
+            (Rack.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
+            (RackReservation.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
+            (Location.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
+            (Device.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
+            (VirtualDeviceContext.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
+            (Cable.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
             # IPAM
-            VRF.objects.restrict(request.user, 'view').filter(tenant=instance),
-            Aggregate.objects.restrict(request.user, 'view').filter(tenant=instance),
-            Prefix.objects.restrict(request.user, 'view').filter(tenant=instance),
-            IPRange.objects.restrict(request.user, 'view').filter(tenant=instance),
-            IPAddress.objects.restrict(request.user, 'view').filter(tenant=instance),
-            ASN.objects.restrict(request.user, 'view').filter(tenant=instance),
-            VLAN.objects.restrict(request.user, 'view').filter(tenant=instance),
-            L2VPN.objects.restrict(request.user, 'view').filter(tenant=instance),
+            (VRF.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
+            (Aggregate.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
+            (Prefix.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
+            (IPRange.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
+            (IPAddress.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
+            (ASN.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
+            (VLAN.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
+            (L2VPN.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
             # Circuits
-            Circuit.objects.restrict(request.user, 'view').filter(tenant=instance),
+            (Circuit.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
             # Virtualization
-            VirtualMachine.objects.restrict(request.user, 'view').filter(tenant=instance),
-            Cluster.objects.restrict(request.user, 'view').filter(tenant=instance),
+            (VirtualMachine.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
+            (Cluster.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
             # Wireless
-            WirelessLAN.objects.restrict(request.user, 'view').filter(tenant=instance),
-            WirelessLink.objects.restrict(request.user, 'view').filter(tenant=instance),
+            (WirelessLAN.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
+            (WirelessLink.objects.restrict(request.user, 'view').filter(tenant=instance), 'tenant_id'),
         ]
 
         return {
@@ -177,6 +187,16 @@ class ContactGroupListView(generic.ObjectListView):
 class ContactGroupView(generic.ObjectView):
     queryset = ContactGroup.objects.all()
 
+    def get_extra_context(self, request, instance):
+        groups = instance.get_descendants(include_self=True)
+        related_models = (
+            (Contact.objects.restrict(request.user, 'view').filter(group__in=groups), 'group_id'),
+        )
+
+        return {
+            'related_models': related_models,
+        }
+
 
 @register_model_view(ContactGroup, 'edit')
 class ContactGroupEditView(generic.ObjectEditView):

+ 10 - 0
netbox/wireless/views.py

@@ -27,6 +27,16 @@ class WirelessLANGroupListView(generic.ObjectListView):
 class WirelessLANGroupView(generic.ObjectView):
     queryset = WirelessLANGroup.objects.all()
 
+    def get_extra_context(self, request, instance):
+        groups = instance.get_descendants(include_self=True)
+        related_models = (
+            (WirelessLAN.objects.restrict(request.user, 'view').filter(group__in=groups), 'group_id'),
+        )
+
+        return {
+            'related_models': related_models,
+        }
+
 
 @register_model_view(WirelessLANGroup, 'edit')
 class WirelessLANGroupEditView(generic.ObjectEditView):