Closes #15794: Make "related objects" dynamic

Instead of hardcoding relationships between models for the detail view,
they are now dynamically generated.
This commit is contained in:
Alexander Haase 2024-04-28 14:25:15 +02:00
parent 85db007ff5
commit 78f741f856
9 changed files with 151 additions and 222 deletions

View File

@ -7,7 +7,7 @@ from netbox.views import generic
from tenancy.views import ObjectContactsView
from utilities.forms import ConfirmationForm
from utilities.utils import count_related
from utilities.views import register_model_view
from utilities.views import GetRelatedModelsMixin, register_model_view
from . import filtersets, forms, tables
from .models import *
@ -26,17 +26,12 @@ class ProviderListView(generic.ObjectListView):
@register_model_view(Provider)
class ProviderView(generic.ObjectView):
class ProviderView(GetRelatedModelsMixin, generic.ObjectView):
queryset = Provider.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(ProviderAccount.objects.restrict(request.user, 'view').filter(provider=instance), 'provider_id'),
(Circuit.objects.restrict(request.user, 'view').filter(provider=instance), 'provider_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
}
@ -92,16 +87,12 @@ class ProviderAccountListView(generic.ObjectListView):
@register_model_view(ProviderAccount)
class ProviderAccountView(generic.ObjectView):
class ProviderAccountView(GetRelatedModelsMixin, generic.ObjectView):
queryset = ProviderAccount.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(Circuit.objects.restrict(request.user, 'view').filter(provider_account=instance), 'provider_account_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
}
@ -156,19 +147,20 @@ class ProviderNetworkListView(generic.ObjectListView):
@register_model_view(ProviderNetwork)
class ProviderNetworkView(generic.ObjectView):
class ProviderNetworkView(GetRelatedModelsMixin, generic.ObjectView):
queryset = ProviderNetwork.objects.all()
def get_extra_context(self, request, instance):
related_models = (
def get_extra_related_models(self, request, instance):
return (
(
Circuit.objects.restrict(request.user, 'view').filter(terminations__provider_network=instance),
'provider_network_id',
),
)
def get_extra_context(self, request, instance):
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance, [CircuitTermination]),
}
@ -215,16 +207,12 @@ class CircuitTypeListView(generic.ObjectListView):
@register_model_view(CircuitType)
class CircuitTypeView(generic.ObjectView):
class CircuitTypeView(GetRelatedModelsMixin, generic.ObjectView):
queryset = CircuitType.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(Circuit.objects.restrict(request.user, 'view').filter(type=instance), 'type_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
}

View File

@ -8,7 +8,7 @@ from netbox.config import get_config, PARAMS
from netbox.views import generic
from netbox.views.generic.base import BaseObjectView
from utilities.utils import count_related
from utilities.views import ContentTypePermissionRequiredMixin, register_model_view
from utilities.views import ContentTypePermissionRequiredMixin, GetRelatedModelsMixin, register_model_view
from . import filtersets, forms, tables
from .models import *
@ -27,16 +27,12 @@ class DataSourceListView(generic.ObjectListView):
@register_model_view(DataSource)
class DataSourceView(generic.ObjectView):
class DataSourceView(GetRelatedModelsMixin, generic.ObjectView):
queryset = DataSource.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(DataFile.objects.restrict(request.user, 'view').filter(source=instance), 'source_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
}

View File

@ -27,7 +27,9 @@ from utilities.paginator import EnhancedPaginator, get_paginate_count
from utilities.permissions import get_permission_for_model
from utilities.query_functions import CollateAsChar
from utilities.utils import count_related
from utilities.views import GetReturnURLMixin, ObjectPermissionRequiredMixin, ViewTab, register_model_view
from utilities.views import (
GetRelatedModelsMixin, GetReturnURLMixin, ObjectPermissionRequiredMixin, ViewTab, register_model_view,
)
from virtualization.models import VirtualMachine
from . import filtersets, forms, tables
from .choices import DeviceFaceChoices
@ -224,19 +226,20 @@ class RegionListView(generic.ObjectListView):
@register_model_view(Region)
class RegionView(generic.ObjectView):
class RegionView(GetRelatedModelsMixin, 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'),
def get_extra_related_models(self, request, regions):
return (
(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'),
)
def get_extra_context(self, request, instance):
regions = instance.get_descendants(include_self=True)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, regions),
}
@ -304,19 +307,20 @@ class SiteGroupListView(generic.ObjectListView):
@register_model_view(SiteGroup)
class SiteGroupView(generic.ObjectView):
class SiteGroupView(GetRelatedModelsMixin, 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'),
def get_extra_related_models(self, request, groups):
return (
(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'),
)
def get_extra_context(self, request, instance):
groups = instance.get_descendants(include_self=True)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, groups),
}
@ -378,31 +382,21 @@ class SiteListView(generic.ObjectListView):
@register_model_view(Site)
class SiteView(generic.ObjectView):
class SiteView(GetRelatedModelsMixin, generic.ObjectView):
queryset = Site.objects.prefetch_related('tenant__group')
def get_extra_context(self, request, instance):
related_models = (
# DCIM
(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
(VirtualMachine.objects.restrict(request.user, 'view').filter(cluster__site=instance), 'site_id'),
# IPAM
(Prefix.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
(ASN.objects.restrict(request.user, 'view').filter(sites=instance), 'site_id'),
def get_extra_related_models(self, request, instance):
return (
(VLANGroup.objects.restrict(request.user, 'view').filter(
scope_type=ContentType.objects.get_for_model(Site),
scope_id=instance.pk
), 'site'),
(VLAN.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'),
# Circuits
(Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).distinct(), 'site_id'),
)
def get_extra_context(self, request, instance):
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance, [CableTermination, CircuitTermination]),
}
@ -464,18 +458,13 @@ class LocationListView(generic.ObjectListView):
@register_model_view(Location)
class LocationView(generic.ObjectView):
class LocationView(GetRelatedModelsMixin, generic.ObjectView):
queryset = Location.objects.all()
def get_extra_context(self, request, instance):
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'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, locations),
}
@ -539,16 +528,12 @@ class RackRoleListView(generic.ObjectListView):
@register_model_view(RackRole)
class RackRoleView(generic.ObjectView):
class RackRoleView(GetRelatedModelsMixin, generic.ObjectView):
queryset = RackRole.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(Rack.objects.restrict(request.user, 'view').filter(role=instance), 'role_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
}
@ -653,15 +638,10 @@ class RackElevationListView(generic.ObjectListView):
@register_model_view(Rack)
class RackView(generic.ObjectView):
class RackView(GetRelatedModelsMixin, generic.ObjectView):
queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'location', 'role')
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'),
)
peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site)
if instance.location:
@ -677,7 +657,7 @@ class RackView(generic.ObjectView):
])
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
'next_rack': next_rack,
'prev_rack': prev_rack,
'svg_extra': svg_extra,
@ -837,19 +817,12 @@ class ManufacturerListView(generic.ObjectListView):
@register_model_view(Manufacturer)
class ManufacturerView(generic.ObjectView):
class ManufacturerView(GetRelatedModelsMixin, generic.ObjectView):
queryset = Manufacturer.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(DeviceType.objects.restrict(request.user, 'view').filter(manufacturer=instance), 'manufacturer_id'),
(ModuleType.objects.restrict(request.user, 'view').filter(manufacturer=instance), 'manufacturer_id'),
(InventoryItem.objects.restrict(request.user, 'view').filter(manufacturer=instance), 'manufacturer_id'),
(Platform.objects.restrict(request.user, 'view').filter(manufacturer=instance), 'manufacturer_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance, [InventoryItemTemplate]),
}
@ -911,16 +884,16 @@ class DeviceTypeListView(generic.ObjectListView):
@register_model_view(DeviceType)
class DeviceTypeView(generic.ObjectView):
class DeviceTypeView(GetRelatedModelsMixin, generic.ObjectView):
queryset = DeviceType.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(Device.objects.restrict(request.user).filter(device_type=instance), 'device_type_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance, [
ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, FrontPortTemplate,
InventoryItemTemplate, InterfaceTemplate, ModuleBayTemplate, PowerOutletTemplate, PowerPortTemplate,
RearPortTemplate,
]),
}
@ -1150,16 +1123,16 @@ class ModuleTypeListView(generic.ObjectListView):
@register_model_view(ModuleType)
class ModuleTypeView(generic.ObjectView):
class ModuleTypeView(GetRelatedModelsMixin, generic.ObjectView):
queryset = ModuleType.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(Module.objects.restrict(request.user).filter(module_type=instance), 'module_type_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance, [
ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, FrontPortTemplate,
InventoryItemTemplate, InterfaceTemplate, ModuleBayTemplate, PowerOutletTemplate, PowerPortTemplate,
RearPortTemplate,
]),
}
@ -1712,17 +1685,12 @@ class DeviceRoleListView(generic.ObjectListView):
@register_model_view(DeviceRole)
class DeviceRoleView(generic.ObjectView):
class DeviceRoleView(GetRelatedModelsMixin, generic.ObjectView):
queryset = DeviceRole.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(Device.objects.restrict(request.user, 'view').filter(role=instance), 'role_id'),
(VirtualMachine.objects.restrict(request.user, 'view').filter(role=instance), 'role_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
}
@ -1776,17 +1744,12 @@ class PlatformListView(generic.ObjectListView):
@register_model_view(Platform)
class PlatformView(generic.ObjectView):
class PlatformView(GetRelatedModelsMixin, generic.ObjectView):
queryset = Platform.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(Device.objects.restrict(request.user, 'view').filter(platform=instance), 'platform_id'),
(VirtualMachine.objects.restrict(request.user, 'view').filter(platform=instance), 'platform_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
}
@ -2140,22 +2103,12 @@ class ModuleListView(generic.ObjectListView):
@register_model_view(Module)
class ModuleView(generic.ObjectView):
class ModuleView(GetRelatedModelsMixin, generic.ObjectView):
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,
'related_models': self.get_related_models(request, instance),
}
@ -3549,16 +3502,12 @@ class PowerPanelListView(generic.ObjectListView):
@register_model_view(PowerPanel)
class PowerPanelView(generic.ObjectView):
class PowerPanelView(GetRelatedModelsMixin, generic.ObjectView):
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,
'related_models': self.get_related_models(request, instance),
}
@ -3662,16 +3611,17 @@ class VirtualDeviceContextListView(generic.ObjectListView):
@register_model_view(VirtualDeviceContext)
class VirtualDeviceContextView(generic.ObjectView):
class VirtualDeviceContextView(GetRelatedModelsMixin, generic.ObjectView):
queryset = VirtualDeviceContext.objects.all()
def get_extra_context(self, request, instance):
related_models = (
def get_extra_related_models(self, request, instance):
return (
(Interface.objects.restrict(request.user, 'view').filter(vdcs__in=[instance]), 'vdc_id'),
)
def get_extra_context(self, request, instance):
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
}

View File

@ -11,7 +11,7 @@ from dcim.models import Interface, Site
from netbox.views import generic
from utilities.tables import get_table_ordering
from utilities.utils import count_related
from utilities.views import ViewTab, register_model_view
from utilities.views import GetRelatedModelsMixin, ViewTab, register_model_view
from virtualization.filtersets import VMInterfaceFilterSet
from virtualization.models import VMInterface
from . import filtersets, forms, tables
@ -33,15 +33,10 @@ class VRFListView(generic.ObjectListView):
@register_model_view(VRF)
class VRFView(generic.ObjectView):
class VRFView(GetRelatedModelsMixin, generic.ObjectView):
queryset = VRF.objects.all()
def get_extra_context(self, request, instance):
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(
instance.import_targets.all(),
orderable=False
@ -52,7 +47,7 @@ class VRFView(generic.ObjectView):
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
'import_targets_table': import_targets_table,
'export_targets_table': export_targets_table,
}
@ -146,16 +141,12 @@ class RIRListView(generic.ObjectListView):
@register_model_view(RIR)
class RIRView(generic.ObjectView):
class RIRView(GetRelatedModelsMixin, generic.ObjectView):
queryset = RIR.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(Aggregate.objects.restrict(request.user, 'view').filter(rir=instance), 'rir_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
}
@ -273,17 +264,12 @@ class ASNListView(generic.ObjectListView):
@register_model_view(ASN)
class ASNView(generic.ObjectView):
class ASNView(GetRelatedModelsMixin, generic.ObjectView):
queryset = ASN.objects.all()
def get_extra_context(self, request, instance):
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 {
'related_models': related_models,
'related_models': self.get_related_models(request, [instance]),
}
@ -422,18 +408,12 @@ class RoleListView(generic.ObjectListView):
@register_model_view(Role)
class RoleView(generic.ObjectView):
class RoleView(GetRelatedModelsMixin, generic.ObjectView):
queryset = Role.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(Prefix.objects.restrict(request.user, 'view').filter(role=instance), 'role_id'),
(IPRange.objects.restrict(request.user, 'view').filter(role=instance), 'role_id'),
(VLAN.objects.restrict(request.user, 'view').filter(role=instance), 'role_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
}
@ -907,16 +887,12 @@ class VLANGroupListView(generic.ObjectListView):
@register_model_view(VLANGroup)
class VLANGroupView(generic.ObjectView):
class VLANGroupView(GetRelatedModelsMixin, generic.ObjectView):
queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
def get_extra_context(self, request, instance):
related_models = (
(VLAN.objects.restrict(request.user, 'view').filter(group=instance), 'group_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
}

View File

@ -3,8 +3,8 @@ from django.shortcuts import get_object_or_404
from django.utils.translation import gettext as _
from netbox.views import generic
from utilities.utils import count_related, get_related_models
from utilities.views import register_model_view, ViewTab
from utilities.utils import count_related
from utilities.views import GetRelatedModelsMixin, register_model_view, ViewTab
from . import filtersets, forms, tables
from .models import *
@ -55,17 +55,14 @@ class TenantGroupListView(generic.ObjectListView):
@register_model_view(TenantGroup)
class TenantGroupView(generic.ObjectView):
class TenantGroupView(GetRelatedModelsMixin, 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,
'related_models': self.get_related_models(request, groups),
}
@ -122,17 +119,12 @@ class TenantListView(generic.ObjectListView):
@register_model_view(Tenant)
class TenantView(generic.ObjectView):
class TenantView(GetRelatedModelsMixin, generic.ObjectView):
queryset = Tenant.objects.all()
def get_extra_context(self, request, instance):
related_models = [
(model.objects.restrict(request.user, 'view').filter(tenant=instance), f'{field}_id')
for model, field in get_related_models(Tenant)
]
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
}
@ -188,17 +180,14 @@ class ContactGroupListView(generic.ObjectListView):
@register_model_view(ContactGroup)
class ContactGroupView(generic.ObjectView):
class ContactGroupView(GetRelatedModelsMixin, 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,
'related_models': self.get_related_models(request, groups),
}
@ -255,16 +244,12 @@ class ContactRoleListView(generic.ObjectListView):
@register_model_view(ContactRole)
class ContactRoleView(generic.ObjectView):
class ContactRoleView(GetRelatedModelsMixin, generic.ObjectView):
queryset = ContactRole.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(ContactAssignment.objects.restrict(request.user, 'view').filter(role=instance), 'role_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
}

View File

@ -1,3 +1,5 @@
from typing import Iterable
from django.contrib.auth.mixins import AccessMixin
from django.core.exceptions import ImproperlyConfigured
from django.urls import reverse
@ -5,10 +7,12 @@ from django.urls.exceptions import NoReverseMatch
from django.utils.translation import gettext_lazy as _
from netbox.registry import registry
from utilities.utils import get_related_models
from .permissions import resolve_permission
__all__ = (
'ContentTypePermissionRequiredMixin',
'GetRelatedModelsMixin',
'GetReturnURLMixin',
'ObjectPermissionRequiredMixin',
'ViewTab',
@ -140,6 +144,51 @@ class GetReturnURLMixin:
return reverse('home')
class GetRelatedModelsMixin:
"""
Provides logic for collecting all related models for the currently viewed model.
"""
def get_extra_related_models(self, request, instance):
"""
Get extra related models for `instance`, which extend `get_related_models`. Can be used to implement custom
lookups for nested and non-direct relationships.
"""
return []
def get_related_models(self, request, instance, omit=[]):
"""
Get related models of the view's `queryset` model without those listed in `omit`. Will be sorted alphabetical.
Args:
request: Current request being processed.
instance: The instance related models should be looked up for. A list of instances can be passed to match
related objects in this list (e.g. to find sites of a region including child regions).
omit: Remove relationships to these models from the result. Needs to be passed, if related models don't
provide a `_list` view.
"""
model = self.queryset.model
related = filter(
lambda m: m[0] is not model and m[0] not in omit,
get_related_models(model, False)
)
related_models = [
(
model.objects.restrict(request.user, 'view').filter(**(
{f'{field}__in': instance}
if isinstance(instance, Iterable)
else {field: instance}
)),
f'{field}_id'
)
for model, field in related
]
related_models.extend(self.get_extra_related_models(request, instance))
return sorted(related_models, key=lambda x: x[0].model._meta.verbose_name.lower())
class ViewTab:
"""
ViewTabs are used for navigation among multiple object-specific views, such as the changelog or journal for

View File

@ -20,7 +20,7 @@ from netbox.views import generic
from tenancy.views import ObjectContactsView
from utilities.query_functions import CollateAsChar
from utilities.utils import count_related
from utilities.views import ViewTab, register_model_view
from utilities.views import GetRelatedModelsMixin, ViewTab, register_model_view
from . import filtersets, forms, tables
from .models import *
@ -39,16 +39,12 @@ class ClusterTypeListView(generic.ObjectListView):
@register_model_view(ClusterType)
class ClusterTypeView(generic.ObjectView):
class ClusterTypeView(GetRelatedModelsMixin, generic.ObjectView):
queryset = ClusterType.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(Cluster.objects.restrict(request.user, 'view').filter(type=instance), 'type_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
}
@ -99,16 +95,12 @@ class ClusterGroupListView(generic.ObjectListView):
@register_model_view(ClusterGroup)
class ClusterGroupView(generic.ObjectView):
class ClusterGroupView(GetRelatedModelsMixin, generic.ObjectView):
queryset = ClusterGroup.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(Cluster.objects.restrict(request.user, 'view').filter(group=instance), 'group_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(request, instance),
}

View File

@ -2,7 +2,7 @@ from ipam.tables import RouteTargetTable
from netbox.views import generic
from tenancy.views import ObjectContactsView
from utilities.utils import count_related
from utilities.views import register_model_view
from utilities.views import GetRelatedModelsMixin, register_model_view
from . import filtersets, forms, tables
from .models import *
@ -21,16 +21,12 @@ class TunnelGroupListView(generic.ObjectListView):
@register_model_view(TunnelGroup)
class TunnelGroupView(generic.ObjectView):
class TunnelGroupView(GetRelatedModelsMixin, generic.ObjectView):
queryset = TunnelGroup.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(Tunnel.objects.restrict(request.user, 'view').filter(group=instance), 'group_id'),
)
return {
'related_models': related_models,
'related_models': self.get_related_models(instance),
}

View File

@ -1,7 +1,7 @@
from dcim.models import Interface
from netbox.views import generic
from utilities.utils import count_related
from utilities.views import register_model_view
from utilities.views import GetRelatedModelsMixin, register_model_view
from . import filtersets, forms, tables
from .models import *
@ -24,17 +24,14 @@ class WirelessLANGroupListView(generic.ObjectListView):
@register_model_view(WirelessLANGroup)
class WirelessLANGroupView(generic.ObjectView):
class WirelessLANGroupView(GetRelatedModelsMixin, 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,
'related_models': self.get_related_models(request, groups),
}