Browse Source

Standard related object links across all models

jeremystretch 3 years ago
parent
commit
8f7c100e22

+ 21 - 0
netbox/circuits/views.py

@@ -29,6 +29,15 @@ class ProviderListView(generic.ObjectListView):
 class ProviderView(generic.ObjectView):
 class ProviderView(generic.ObjectView):
     queryset = Provider.objects.all()
     queryset = Provider.objects.all()
 
 
+    def get_extra_context(self, request, instance):
+        related_models = (
+            (Circuit.objects.restrict(request.user, 'view').filter(provider=instance), 'provider_id'),
+        )
+
+        return {
+            'related_models': related_models,
+        }
+
 
 
 @register_model_view(Provider, 'edit')
 @register_model_view(Provider, 'edit')
 class ProviderEditView(generic.ObjectEditView):
 class ProviderEditView(generic.ObjectEditView):
@@ -79,6 +88,18 @@ class ProviderNetworkListView(generic.ObjectListView):
 class ProviderNetworkView(generic.ObjectView):
 class ProviderNetworkView(generic.ObjectView):
     queryset = ProviderNetwork.objects.all()
     queryset = ProviderNetwork.objects.all()
 
 
+    def get_extra_context(self, request, instance):
+        related_models = (
+            (
+                Circuit.objects.restrict(request.user, 'view').filter(terminations__provider_network=instance),
+                'providernetwork_id',
+            ),
+        )
+
+        return {
+            'related_models': related_models,
+        }
+
 
 
 @register_model_view(ProviderNetwork, 'edit')
 @register_model_view(ProviderNetwork, 'edit')
 class ProviderNetworkEditView(generic.ObjectEditView):
 class ProviderNetworkEditView(generic.ObjectEditView):

+ 59 - 29
netbox/dcim/views.py

@@ -21,9 +21,7 @@ from utilities.paginator import EnhancedPaginator, get_paginate_count
 from utilities.permissions import get_permission_for_model
 from utilities.permissions import get_permission_for_model
 from utilities.utils import count_related
 from utilities.utils import count_related
 from utilities.views import GetReturnURLMixin, ObjectPermissionRequiredMixin, ViewTab, register_model_view
 from utilities.views import GetReturnURLMixin, ObjectPermissionRequiredMixin, ViewTab, register_model_view
-from virtualization.filtersets import VirtualMachineFilterSet
 from virtualization.models import VirtualMachine
 from virtualization.models import VirtualMachine
-from virtualization.tables import VirtualMachineTable
 from . import filtersets, forms, tables
 from . import filtersets, forms, tables
 from .choices import DeviceFaceChoices
 from .choices import DeviceFaceChoices
 from .constants import NONCONNECTABLE_IFACE_TYPES
 from .constants import NONCONNECTABLE_IFACE_TYPES
@@ -359,24 +357,24 @@ class SiteView(generic.ObjectView):
     queryset = Site.objects.prefetch_related('tenant__group')
     queryset = Site.objects.prefetch_related('tenant__group')
 
 
     def get_extra_context(self, request, instance):
     def get_extra_context(self, request, instance):
-        related_models = [
+        related_models = (
             # DCIM
             # DCIM
-            Location.objects.restrict(request.user, 'view').filter(site=instance),
-            Rack.objects.restrict(request.user, 'view').filter(site=instance),
-            Device.objects.restrict(request.user, 'view').filter(site=instance),
+            (Location.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
+            (Rack.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
+            (Device.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
             # Virtualization
             # Virtualization
-            VirtualMachine.objects.restrict(request.user, 'view').filter(cluster__site=instance),
+            (VirtualMachine.objects.restrict(request.user, 'view').filter(cluster__site=instance), 'site_id'),
             # IPAM
             # IPAM
-            Prefix.objects.restrict(request.user, 'view').filter(site=instance),
-            ASN.objects.restrict(request.user, 'view').filter(sites=instance),
-            VLANGroup.objects.restrict(request.user, 'view').filter(
+            (Prefix.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
+            (ASN.objects.restrict(request.user, 'view').filter(sites=instance), 'site_id'),
+            (VLANGroup.objects.restrict(request.user, 'view').filter(
                 scope_type=ContentType.objects.get_for_model(Site),
                 scope_type=ContentType.objects.get_for_model(Site),
                 scope_id=instance.pk
                 scope_id=instance.pk
-            ),
-            VLAN.objects.restrict(request.user, 'view').filter(site=instance),
+            ), 'site_id'),
+            (VLAN.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
             # Circuits
             # Circuits
-            Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).distinct(),
-        ]
+            (Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).distinct(), 'site_id'),
+        )
 
 
         locations = Location.objects.add_related_count(
         locations = Location.objects.add_related_count(
             Location.objects.all(),
             Location.objects.all(),
@@ -658,6 +656,11 @@ class RackView(generic.ObjectView):
     queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'location', 'role')
     queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'location', 'role')
 
 
     def get_extra_context(self, request, instance):
     def get_extra_context(self, request, instance):
+        related_models = (
+            (Device.objects.restrict(request.user, 'view').filter(rack=instance), 'rack_id'),
+            (PowerFeed.objects.restrict(request.user).filter(rack=instance), 'rack_id'),
+        )
+
         # Get 0U devices located within the rack
         # Get 0U devices located within the rack
         nonracked_devices = Device.objects.filter(
         nonracked_devices = Device.objects.filter(
             rack=instance,
             rack=instance,
@@ -675,11 +678,6 @@ class RackView(generic.ObjectView):
         prev_rack = peer_racks.filter(_name__lt=instance._name).reverse().first()
         prev_rack = peer_racks.filter(_name__lt=instance._name).reverse().first()
 
 
         reservations = RackReservation.objects.restrict(request.user, 'view').filter(rack=instance)
         reservations = RackReservation.objects.restrict(request.user, 'view').filter(rack=instance)
-        power_feeds = PowerFeed.objects.restrict(request.user, 'view').filter(rack=instance).prefetch_related(
-            'power_panel'
-        )
-
-        device_count = Device.objects.restrict(request.user, 'view').filter(rack=instance).count()
 
 
         # Determine any additional parameters to pass when embedding the rack elevations
         # Determine any additional parameters to pass when embedding the rack elevations
         svg_extra = '&'.join([
         svg_extra = '&'.join([
@@ -687,9 +685,8 @@ class RackView(generic.ObjectView):
         ])
         ])
 
 
         return {
         return {
-            'device_count': device_count,
+            'related_models': related_models,
             'reservations': reservations,
             'reservations': reservations,
-            'power_feeds': power_feeds,
             'nonracked_devices': nonracked_devices,
             'nonracked_devices': nonracked_devices,
             'next_rack': next_rack,
             'next_rack': next_rack,
             'prev_rack': prev_rack,
             'prev_rack': prev_rack,
@@ -881,10 +878,12 @@ class DeviceTypeView(generic.ObjectView):
     queryset = DeviceType.objects.all()
     queryset = DeviceType.objects.all()
 
 
     def get_extra_context(self, request, instance):
     def get_extra_context(self, request, instance):
-        instance_count = Device.objects.restrict(request.user).filter(device_type=instance).count()
+        related_models = (
+            (Device.objects.restrict(request.user).filter(device_type=instance), 'device_type_id'),
+        )
 
 
         return {
         return {
-            'instance_count': instance_count,
+            'related_models': related_models,
         }
         }
 
 
 
 
@@ -1119,10 +1118,12 @@ class ModuleTypeView(generic.ObjectView):
     queryset = ModuleType.objects.all()
     queryset = ModuleType.objects.all()
 
 
     def get_extra_context(self, request, instance):
     def get_extra_context(self, request, instance):
-        instance_count = Module.objects.restrict(request.user).filter(module_type=instance).count()
+        related_models = (
+            (Module.objects.restrict(request.user).filter(module_type=instance), 'module_type_id'),
+        )
 
 
         return {
         return {
-            'instance_count': instance_count,
+            'related_models': related_models,
         }
         }
 
 
 
 
@@ -1807,13 +1808,9 @@ class DeviceView(generic.ObjectView):
             vc_members = []
             vc_members = []
 
 
         services = Service.objects.restrict(request.user, 'view').filter(device=instance)
         services = Service.objects.restrict(request.user, 'view').filter(device=instance)
-        vdcs = VirtualDeviceContext.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
-            'tenant'
-        )
 
 
         return {
         return {
             'services': services,
             'services': services,
-            'vdcs': vdcs,
             'vc_members': vc_members,
             'vc_members': vc_members,
             'svg_extra': f'highlight=id:{instance.pk}'
             'svg_extra': f'highlight=id:{instance.pk}'
         }
         }
@@ -2114,6 +2111,21 @@ class ModuleListView(generic.ObjectListView):
 class ModuleView(generic.ObjectView):
 class ModuleView(generic.ObjectView):
     queryset = Module.objects.all()
     queryset = Module.objects.all()
 
 
+    def get_extra_context(self, request, instance):
+        related_models = (
+            (Interface.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
+            (ConsolePort.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
+            (ConsoleServerPort.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
+            (PowerPort.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
+            (PowerOutlet.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
+            (FrontPort.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
+            (RearPort.objects.restrict(request.user, 'view').filter(module=instance), 'module_id'),
+        )
+
+        return {
+            'related_models': related_models,
+        }
+
 
 
 @register_model_view(Module, 'edit')
 @register_model_view(Module, 'edit')
 class ModuleEditView(generic.ObjectEditView):
 class ModuleEditView(generic.ObjectEditView):
@@ -3436,6 +3448,15 @@ class PowerPanelListView(generic.ObjectListView):
 class PowerPanelView(generic.ObjectView):
 class PowerPanelView(generic.ObjectView):
     queryset = PowerPanel.objects.all()
     queryset = PowerPanel.objects.all()
 
 
+    def get_extra_context(self, request, instance):
+        related_models = (
+            (PowerFeed.objects.restrict(request.user).filter(power_panel=instance), 'power_panel_id'),
+        )
+
+        return {
+            'related_models': related_models,
+        }
+
 
 
 @register_model_view(PowerPanel, 'edit')
 @register_model_view(PowerPanel, 'edit')
 class PowerPanelEditView(generic.ObjectEditView):
 class PowerPanelEditView(generic.ObjectEditView):
@@ -3537,6 +3558,15 @@ class VirtualDeviceContextListView(generic.ObjectListView):
 class VirtualDeviceContextView(generic.ObjectView):
 class VirtualDeviceContextView(generic.ObjectView):
     queryset = VirtualDeviceContext.objects.all()
     queryset = VirtualDeviceContext.objects.all()
 
 
+    def get_extra_context(self, request, instance):
+        related_models = (
+            (Interface.objects.restrict(request.user, 'view').filter(vdcs__in=[instance]), 'vdc_id'),
+        )
+
+        return {
+            'related_models': related_models,
+        }
+
 
 
 @register_model_view(VirtualDeviceContext, 'edit')
 @register_model_view(VirtualDeviceContext, 'edit')
 class VirtualDeviceContextEditView(generic.ObjectEditView):
 class VirtualDeviceContextEditView(generic.ObjectEditView):

+ 10 - 9
netbox/ipam/views.py

@@ -37,8 +37,10 @@ class VRFView(generic.ObjectView):
     queryset = VRF.objects.all()
     queryset = VRF.objects.all()
 
 
     def get_extra_context(self, request, instance):
     def get_extra_context(self, request, instance):
-        prefix_count = Prefix.objects.restrict(request.user, 'view').filter(vrf=instance).count()
-        ipaddress_count = IPAddress.objects.restrict(request.user, 'view').filter(vrf=instance).count()
+        related_models = (
+            (Prefix.objects.restrict(request.user, 'view').filter(vrf=instance), 'vrf_id'),
+            (IPAddress.objects.restrict(request.user, 'view').filter(vrf=instance), 'vrf_id'),
+        )
 
 
         import_targets_table = tables.RouteTargetTable(
         import_targets_table = tables.RouteTargetTable(
             instance.import_targets.all(),
             instance.import_targets.all(),
@@ -50,8 +52,7 @@ class VRFView(generic.ObjectView):
         )
         )
 
 
         return {
         return {
-            'prefix_count': prefix_count,
-            'ipaddress_count': ipaddress_count,
+            'related_models': related_models,
             'import_targets_table': import_targets_table,
             'import_targets_table': import_targets_table,
             'export_targets_table': export_targets_table,
             'export_targets_table': export_targets_table,
         }
         }
@@ -228,12 +229,13 @@ class ASNView(generic.ObjectView):
     queryset = ASN.objects.all()
     queryset = ASN.objects.all()
 
 
     def get_extra_context(self, request, instance):
     def get_extra_context(self, request, instance):
-        sites = instance.sites.restrict(request.user, 'view')
-        providers = instance.providers.restrict(request.user, 'view')
+        related_models = (
+            (Site.objects.restrict(request.user, 'view').filter(asns__in=[instance]), 'asn_id'),
+            (Provider.objects.restrict(request.user, 'view').filter(asns__in=[instance]), 'asn_id'),
+        )
 
 
         return {
         return {
-            'sites_count': sites.count(),
-            'providers_count': providers.count(),
+            'related_models': related_models,
         }
         }
 
 
 
 
@@ -868,7 +870,6 @@ class VLANGroupView(generic.ObjectView):
             Prefetch('prefixes', queryset=Prefix.objects.restrict(request.user)),
             Prefetch('prefixes', queryset=Prefix.objects.restrict(request.user)),
             'tenant', 'site', 'role',
             'tenant', 'site', 'role',
         ).order_by('vid')
         ).order_by('vid')
-        vlans_count = vlans.count()
         vlans = add_available_vlans(vlans, vlan_group=instance)
         vlans = add_available_vlans(vlans, vlan_group=instance)
 
 
         vlans_table = tables.VLANTable(vlans, user=request.user, exclude=('group',))
         vlans_table = tables.VLANTable(vlans, user=request.user, exclude=('group',))

+ 1 - 1
netbox/templates/circuits/circuittype.html

@@ -35,7 +35,7 @@
     {% plugin_left_page object %}
     {% plugin_left_page object %}
   </div>
   </div>
 	<div class="col col-md-6">
 	<div class="col col-md-6">
-      {% include 'inc/panels/related_objects.html' %}
+    {% include 'inc/panels/related_objects.html' %}
     {% include 'inc/panels/custom_fields.html' %}
     {% include 'inc/panels/custom_fields.html' %}
     {% plugin_right_page object %}
     {% plugin_right_page object %}
 	</div>
 	</div>

+ 2 - 7
netbox/templates/circuits/provider.html

@@ -37,21 +37,16 @@
                         <th scope="row">Description</th>
                         <th scope="row">Description</th>
                         <td>{{ object.description|placeholder }}</td>
                         <td>{{ object.description|placeholder }}</td>
                     </tr>
                     </tr>
-                    <tr>
-                        <th scope="row">Circuits</th>
-                        <td>
-                            <a href="{% url 'circuits:circuit_list' %}?provider={{ object.slug }}">{{ object.circuits.count }}</a>
-                        </td>
-                    </tr>
                 </table>
                 </table>
             </div>
             </div>
         </div>
         </div>
         {% include 'inc/panels/tags.html' %}
         {% include 'inc/panels/tags.html' %}
+        {% include 'inc/panels/comments.html' %}
         {% plugin_left_page object %}
         {% plugin_left_page object %}
     </div>
     </div>
     <div class="col col-md-6">
     <div class="col col-md-6">
+        {% include 'inc/panels/related_objects.html' %}
         {% include 'inc/panels/custom_fields.html' %}
         {% include 'inc/panels/custom_fields.html' %}
-        {% include 'inc/panels/comments.html' %}
         {% include 'inc/panels/contacts.html' %}
         {% include 'inc/panels/contacts.html' %}
         {% plugin_right_page object %}
         {% plugin_right_page object %}
     </div>
     </div>

+ 3 - 2
netbox/templates/circuits/providernetwork.html

@@ -37,12 +37,13 @@
                 </table>
                 </table>
             </div>
             </div>
         </div>
         </div>
+        {% include 'inc/panels/tags.html' %}
         {% plugin_left_page object %}
         {% plugin_left_page object %}
     </div>
     </div>
     <div class="col col-md-6">
     <div class="col col-md-6">
-        {% include 'inc/panels/custom_fields.html' %}
-        {% include 'inc/panels/tags.html' %}
+        {% include 'inc/panels/related_objects.html' %}
         {% include 'inc/panels/comments.html' %}
         {% include 'inc/panels/comments.html' %}
+        {% include 'inc/panels/custom_fields.html' %}
         {% plugin_right_page object %}
         {% plugin_right_page object %}
     </div>
     </div>
 </div>
 </div>

+ 4 - 22
netbox/templates/dcim/device.html

@@ -157,28 +157,10 @@
             {% include 'inc/panels/comments.html' %}
             {% include 'inc/panels/comments.html' %}
             <div class="card">
             <div class="card">
               <h5 class="card-header">Virtual Device Contexts</h5>
               <h5 class="card-header">Virtual Device Contexts</h5>
-              <div class="card-body">
-                {% if vdcs %}
-                  <table class="table table-hover">
-                    <tr>
-                      <th>Name</th>
-                      <th>Status</th>
-                      <th>Identifier</th>
-                      <th>Tenant</th>
-                    </tr>
-                    {% for vdc in vdcs %}
-                      <tr>
-                        <td>{{ vdc|linkify }}</td>
-                        <td>{% badge vdc.get_status_display bg_color=vdc.get_status_color %}</td>
-                        <td>{{ vdc.identifier|placeholder }}</td>
-                        <td>{{ vdc.tenant|linkify|placeholder }}</td>
-                      </tr>
-                    {% endfor %}
-                  </table>
-                {% else %}
-                  <div class="text-muted">None</div>
-                {% endif %}
-              </div>
+              <div class="card-body htmx-container table-responsive"
+                hx-get="{% url 'dcim:virtualdevicecontext_list' %}?device_id={{ object.pk }}"
+                hx-trigger="load"
+              ></div>
               {% if perms.dcim.add_virtualdevicecontext %}
               {% if perms.dcim.add_virtualdevicecontext %}
                 <div class="card-footer text-end noprint">
                 <div class="card-footer text-end noprint">
                   <a href="{% url 'dcim:virtualdevicecontext_add' %}?device={{ object.pk }}" class="btn btn-sm btn-primary">
                   <a href="{% url 'dcim:virtualdevicecontext_add' %}?device={{ object.pk }}" class="btn btn-sm btn-primary">

+ 2 - 5
netbox/templates/dcim/devicetype.html

@@ -85,18 +85,15 @@
                                 {% endif %}
                                 {% endif %}
                             </td>
                             </td>
                         </tr>
                         </tr>
-                        <tr>
-                            <td>Instances</td>
-                            <td><a href="{% url 'dcim:device_list' %}?device_type_id={{ object.pk }}">{{ instance_count }}</a></td>
-                        </tr>
                     </table>
                     </table>
                 </div>
                 </div>
             </div>
             </div>
+            {% include 'inc/panels/tags.html' %}
             {% plugin_left_page object %}
             {% plugin_left_page object %}
         </div>
         </div>
         <div class="col col-md-6">
         <div class="col col-md-6">
+            {% include 'inc/panels/related_objects.html' %}
             {% include 'inc/panels/custom_fields.html' %}
             {% include 'inc/panels/custom_fields.html' %}
-            {% include 'inc/panels/tags.html' %}
             {% include 'inc/panels/comments.html' %}
             {% include 'inc/panels/comments.html' %}
             {% plugin_right_page object %}
             {% plugin_right_page object %}
         </div>
         </div>

+ 5 - 95
netbox/templates/dcim/module.html

@@ -81,104 +81,14 @@
         </table>
         </table>
       </div>
       </div>
     </div>
     </div>
-    {% include 'inc/panels/custom_fields.html' %}
     {% include 'inc/panels/tags.html' %}
     {% include 'inc/panels/tags.html' %}
     {% include 'inc/panels/comments.html' %}
     {% include 'inc/panels/comments.html' %}
     {% plugin_left_page object %}
     {% plugin_left_page object %}
-    </div>
-    <div class="col col-md-6">
-      <div class="card">
-        <h5 class="card-header">Components</h5>
-        <div class="card-body">
-          <table class="table table-hover attr-table">
-            <tr>
-              <th scope="row">Interfaces</th>
-              <td>
-                {% with component_count=object.interfaces.count %}
-                  {% if component_count %}
-                    <a href="{% url 'dcim:interface_list' %}?module_id={{ object.pk }}">{{ component_count }}</a>
-                  {% else %}
-                    {{ ''|placeholder }}
-                  {% endif %}
-                {% endwith %}
-              </td>
-            </tr>
-            <tr>
-              <th scope="row">Console Ports</th>
-              <td>
-                {% with component_count=object.consoleports.count %}
-                  {% if component_count %}
-                    <a href="{% url 'dcim:consoleport_list' %}?module_id={{ object.pk }}">{{ component_count }}</a>
-                  {% else %}
-                    {{ ''|placeholder }}
-                  {% endif %}
-                {% endwith %}
-              </td>
-            </tr>
-            <tr>
-              <th scope="row">Console Server Ports</th>
-              <td>
-                {% with component_count=object.consoleserverports.count %}
-                  {% if component_count %}
-                    <a href="{% url 'dcim:consoleserverport_list' %}?module_id={{ object.pk }}">{{ component_count }}</a>
-                  {% else %}
-                    {{ ''|placeholder }}
-                  {% endif %}
-                {% endwith %}
-              </td>
-            </tr>
-            <tr>
-              <th scope="row">Power Ports</th>
-              <td>
-                {% with component_count=object.powerports.count %}
-                  {% if component_count %}
-                    <a href="{% url 'dcim:powerport_list' %}?module_id={{ object.pk }}">{{ component_count }}</a>
-                  {% else %}
-                    {{ ''|placeholder }}
-                  {% endif %}
-                {% endwith %}
-              </td>
-            </tr>
-            <tr>
-              <th scope="row">Power Outlets</th>
-              <td>
-                {% with component_count=object.poweroutlets.count %}
-                  {% if component_count %}
-                    <a href="{% url 'dcim:poweroutlet_list' %}?module_id={{ object.pk }}">{{ component_count }}</a>
-                  {% else %}
-                    {{ ''|placeholder }}
-                  {% endif %}
-                {% endwith %}
-              </td>
-            </tr>
-            <tr>
-              <th scope="row">Front Ports</th>
-              <td>
-                {% with component_count=object.frontports.count %}
-                  {% if component_count %}
-                    <a href="{% url 'dcim:frontport_list' %}?module_id={{ object.pk }}">{{ component_count }}</a>
-                  {% else %}
-                    {{ ''|placeholder }}
-                  {% endif %}
-                {% endwith %}
-              </td>
-            </tr>
-            <tr>
-              <th scope="row">Rear Ports</th>
-              <td>
-                {% with component_count=object.rearports.count %}
-                  {% if component_count %}
-                    <a href="{% url 'dcim:rearport_list' %}?module_id={{ object.pk }}">{{ component_count }}</a>
-                  {% else %}
-                    {{ ''|placeholder }}
-                  {% endif %}
-                {% endwith %}
-              </td>
-            </tr>
-          </table>
-        </div>
-      </div>
-      {% plugin_right_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>
 </div>
 <div class="row">
 <div class="row">

+ 4 - 7
netbox/templates/dcim/moduletype.html

@@ -36,19 +36,16 @@
                 {% endif %}
                 {% endif %}
                 </td>
                 </td>
             </tr>
             </tr>
-            <tr>
-              <td>Instances</td>
-              <td><a href="{% url 'dcim:module_list' %}?module_type_id={{ object.pk }}">{{ instance_count }}</a></td>
-            </tr>
           </table>
           </table>
         </div>
         </div>
       </div>
       </div>
-      {% include 'inc/panels/custom_fields.html' %}
+      {% include 'inc/panels/tags.html' %}
+      {% include 'inc/panels/comments.html' %}
       {% plugin_left_page object %}
       {% plugin_left_page object %}
     </div>
     </div>
     <div class="col col-md-6">
     <div class="col col-md-6">
-      {% include 'inc/panels/tags.html' %}
-      {% include 'inc/panels/comments.html' %}
+      {% include 'inc/panels/related_objects.html' %}
+      {% include 'inc/panels/custom_fields.html' %}
       {% include 'inc/panels/image_attachments.html' %}
       {% include 'inc/panels/image_attachments.html' %}
       {% plugin_right_page object %}
       {% plugin_right_page object %}
     </div>
     </div>

+ 6 - 5
netbox/templates/dcim/powerpanel.html

@@ -38,11 +38,12 @@
     {% plugin_left_page object %}
     {% plugin_left_page object %}
   </div>
   </div>
 	<div class="col col-md-6">
 	<div class="col col-md-6">
-        {% include 'inc/panels/custom_fields.html' %}
-        {% include 'inc/panels/contacts.html' %}
-        {% include 'inc/panels/image_attachments.html' %}
-        {% plugin_right_page object %}
-    </div>
+    {% include 'inc/panels/related_objects.html' %}
+    {% include 'inc/panels/custom_fields.html' %}
+    {% include 'inc/panels/contacts.html' %}
+    {% include 'inc/panels/image_attachments.html' %}
+    {% plugin_right_page object %}
+  </div>
 </div>
 </div>
 <div class="row my-3">
 <div class="row my-3">
   <div class="col col-md-12">
   <div class="col col-md-12">

+ 1 - 40
netbox/templates/dcim/rack.html

@@ -90,12 +90,6 @@
                         <th scope="row">Asset Tag</th>
                         <th scope="row">Asset Tag</th>
                         <td class="font-monospace">{{ object.asset_tag|placeholder }}</td>
                         <td class="font-monospace">{{ object.asset_tag|placeholder }}</td>
                     </tr>
                     </tr>
-                    <tr>
-                        <th scope="row">Devices</th>
-                        <td>
-                            <a href="{% url 'dcim:device_list' %}?rack_id={{ object.id }}">{{ device_count }}</a>
-                        </td>
-                    </tr>
                     <tr>
                     <tr>
                         <th scope="row">Space Utilization</th>
                         <th scope="row">Space Utilization</th>
                         <td>{% utilization_graph object.get_utilization %}</td>
                         <td>{% utilization_graph object.get_utilization %}</td>
@@ -192,40 +186,6 @@
         {% include 'inc/panels/custom_fields.html' %}
         {% include 'inc/panels/custom_fields.html' %}
         {% include 'inc/panels/tags.html' %}
         {% include 'inc/panels/tags.html' %}
         {% include 'inc/panels/comments.html' %}
         {% include 'inc/panels/comments.html' %}
-        {% if power_feeds %}
-            <div class="card">
-                <h5 class="card-header">
-                    Power Feeds
-                </h5>
-                <div class="card-body">
-                    <table class="table">
-                        <tr>
-                            <th>Panel</th>
-                            <th>Feed</th>
-                            <th>Status</th>
-                            <th>Type</th>
-                            <th>Utilization</th>
-                        </tr>
-                        {% for powerfeed in power_feeds %}
-                            <tr>
-                                <td>{{ powerfeed.power_panel|linkify }}</td>
-                                <td>{{ powerfeed|linkify }}</td>
-                                <td>{% badge powerfeed.get_status_display bg_color=powerfeed.get_status_color %}</td>
-                                <td>{% badge powerfeed.get_type_display bg_color=powerfeed.get_type_color %}</td>
-                                {% with power_port=powerfeed.connected_endpoints.0 %}
-                                    {% if power_port %}
-                                        <td>{% utilization_graph power_port.get_power_draw.allocated|percentage:powerfeed.available_power %}</td>
-                                    {% else %}
-                                        <td class="text-muted">N/A</td>
-                                    {% endif %}
-                                {% endwith %}
-                            </tr>
-                        {% endfor %}
-                    </table>
-                </div>
-            </div>
-        {% endif %}
-
         {% include 'inc/panels/image_attachments.html' %}
         {% include 'inc/panels/image_attachments.html' %}
         <div class="card">
         <div class="card">
             <h5 class="card-header">
             <h5 class="card-header">
@@ -300,6 +260,7 @@
               </div>
               </div>
             </div>
             </div>
         </div>
         </div>
+        {% include 'inc/panels/related_objects.html' %}
         {% include 'dcim/inc/nonracked_devices.html' %}
         {% include 'dcim/inc/nonracked_devices.html' %}
         {% include 'inc/panels/contacts.html' %}
         {% include 'inc/panels/contacts.html' %}
         {% plugin_right_page object %}
         {% plugin_right_page object %}

+ 2 - 1
netbox/templates/dcim/virtualdevicecontext.html

@@ -59,10 +59,11 @@
       </div>
       </div>
     </div>
     </div>
     {% plugin_left_page object %}
     {% plugin_left_page object %}
+    {% include 'inc/panels/tags.html' %}
   </div>
   </div>
   <div class="col col-md-6">
   <div class="col col-md-6">
+    {% include 'inc/panels/related_objects.html' %}
     {% include 'inc/panels/comments.html' %}
     {% include 'inc/panels/comments.html' %}
-    {% include 'inc/panels/tags.html' %}
     {% include 'inc/panels/custom_fields.html' %}
     {% include 'inc/panels/custom_fields.html' %}
     {% plugin_right_page object %}
     {% plugin_right_page object %}
   </div>
   </div>

+ 2 - 35
netbox/templates/ipam/asn.html

@@ -39,54 +39,21 @@
               <td>Description</td>
               <td>Description</td>
               <td>{{ object.description|placeholder }}</td>
               <td>{{ object.description|placeholder }}</td>
             </tr>
             </tr>
-            <tr>
-              <td>Sites</td>
-              <td>
-                {% if sites_count %}
-                  <a href="{% url 'dcim:site_list' %}?asn_id={{ object.pk }}">{{ sites_count }}</a>
-                {% else %}
-                  {{ ''|placeholder }}
-                {% endif %}
-              </td>
-            </tr>
-            <tr>
-              <td>Providers</td>
-              <td>
-                {% if providers_count %}
-                  <a href="{% url 'circuits:provider_list' %}?asn_id={{ object.pk }}">{{ providers_count }}</a>
-                {% else %}
-                  {{ ''|placeholder }}
-                {% endif %}
-              </td>
-            </tr>
           </table>
           </table>
         </div>
         </div>
       </div>
       </div>
       {% plugin_left_page object %}
       {% plugin_left_page object %}
+      {% include 'inc/panels/tags.html' %}
     </div>
     </div>
     <div class="col col-md-6">
     <div class="col col-md-6">
+      {% include 'inc/panels/related_objects.html' %}
       {% include 'inc/panels/custom_fields.html' %}
       {% include 'inc/panels/custom_fields.html' %}
-      {% include 'inc/panels/tags.html' with tags=object.tags.all url='ipam:asn_list' %}
       {% include 'inc/panels/comments.html' %}
       {% include 'inc/panels/comments.html' %}
       {% plugin_right_page object %}
       {% plugin_right_page object %}
     </div>
     </div>
   </div>
   </div>
   <div class="row">
   <div class="row">
     <div class="col col-md-12">
     <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' %}?asn_id={{ object.pk }}"
-          hx-trigger="load"
-        ></div>
-      </div>
-      <div class="card">
-        <h5 class="card-header">Providers</h5>
-        <div class="card-body htmx-container table-responsive"
-          hx-get="{% url 'circuits:provider_list' %}?asn_id={{ object.pk }}"
-          hx-trigger="load"
-        ></div>
-      </div>
       {% plugin_full_width_page object %}
       {% plugin_full_width_page object %}
     </div>
     </div>
   </div>
   </div>

+ 2 - 13
netbox/templates/ipam/vrf.html

@@ -35,25 +35,14 @@
                       <th scope="row">Description</th>
                       <th scope="row">Description</th>
                       <td>{{ object.description|placeholder }}</td>
                       <td>{{ object.description|placeholder }}</td>
                   </tr>
                   </tr>
-                  <tr>
-                      <th scope="row">Prefixes</th>
-                      <td>
-                          <a href="{% url 'ipam:prefix_list' %}?vrf_id={{ object.pk }}">{{ prefix_count }}</a>
-                      </td>
-                  </tr>
-                  <tr>
-                      <th scope="row">IP Addresses</th>
-                      <td>
-                          <a href="{% url 'ipam:ipaddress_list' %}?vrf_id={{ object.pk }}">{{ ipaddress_count }}</a>
-                      </td>
-                  </tr>
               </table>
               </table>
           </div>
           </div>
       </div>
       </div>
+      {% include 'inc/panels/tags.html' %}
       {% plugin_left_page object %}
       {% plugin_left_page object %}
   </div>
   </div>
   <div class="col col-md-6">
   <div class="col col-md-6">
-    {% include 'inc/panels/tags.html' %}
+    {% include 'inc/panels/related_objects.html' %}
     {% include 'inc/panels/custom_fields.html' %}
     {% include 'inc/panels/custom_fields.html' %}
     {% include 'inc/panels/comments.html' %}
     {% include 'inc/panels/comments.html' %}
     {% plugin_right_page object %}
     {% plugin_right_page object %}

+ 0 - 4
netbox/templates/virtualization/cluster.html

@@ -44,10 +44,6 @@
                     <th scope="row">Site</th>
                     <th scope="row">Site</th>
                     <td>{{ object.site|linkify|placeholder }}</td>
                     <td>{{ object.site|linkify|placeholder }}</td>
                 </tr>
                 </tr>
-                <tr>
-                    <th scope="row">Virtual Machines</th>
-                    <td><a href="{% url 'virtualization:virtualmachine_list' %}?cluster_id={{ object.pk }}">{{ object.virtual_machines.count }}</a></td>
-                </tr>
             </table>
             </table>
         </div>
         </div>
     </div>
     </div>