mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-23 04:22:01 -06:00
Standardize related model display for nested models
This commit is contained in:
parent
0c9e7aa074
commit
91b81d51da
@ -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(),
|
||||
}
|
||||
|
@ -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' %}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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 %}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user